Start small, then add structure
A one-file Ruby script is already a valid project. As it grows, separate code you want to reuse from the script that starts the program, then add tests and dependency files when needed. Ruby itself does not require one universal folder layout. The structure below is a simple example for a command-line application, not a rule every project must follow.
Rails applications have their own conventions, such as app, config and db directories. Do not copy a Rails tree into a small standalone Ruby script just because both use the Ruby language.
An illustrative small Ruby application
ruby-notes/
bin/notes
lib/notes.rb
test/notes_test.rb
Gemfile
Gemfile.lock
.ruby-version
.gitignore
README.mdSource files and the entry script
Files ending in .rb hold Ruby source. In this example, lib/notes.rb defines a small reusable module. The script bin/notes is the entry point: it loads that module, calls it and prints the result. A bin or exe directory often holds commands people run, but the name is a convention. You can execute this script explicitly with ruby bin/notes, including on Windows, whether or not it has a file extension.
The directory names describe purpose rather than changing Ruby syntax. You might instead put a tiny application entirely in hello.rb. Split a file only when a second file makes the program easier to understand or reuse.
lib/notes.rb — reusable behaviour
module Notes
def self.title(name)
"Note: #{name}"
end
endbin/notes — program entry point
require_relative "../lib/notes"
puts Notes.title("Ruby")Terminal — from ruby-notes
ruby bin/notesrequire and require_relative
require loads a feature from Ruby’s load path, usually a standard library or an installed gem: require "json" or, after adding and installing a dependency, require "rainbow". Ruby normally loads a required feature only once per process. Writing require does not install a missing gem.
require_relative loads a file relative to the file containing that statement. From bin/notes, ../lib/notes means go up from bin and then into lib. You can omit the .rb extension. This is a clear choice for another source file in your own project. A bare require "notes" is not automatically the same thing; it needs an appropriate load path or project packaging setup.
Gemfile, Gemfile.lock and a gemspec
If the application uses external gems, place a Gemfile at its root. It lists direct dependencies and their sources. Run bundle install there; Bundler resolves and installs them and writes Gemfile.lock with exact versions. For an application, commit both files so another developer can reproduce the tested dependency set. If the project needs no external gems, it can start without either file.
A published reusable gem usually also has a .gemspec file declaring package metadata and runtime dependencies. That is a different job from a small application’s Gemfile. An application that uses a gem resolves dependency versions in its own bundle; a library cannot force consumers to use its development lockfile.
Configuration and tests
A .ruby-version file can record the Ruby release a version manager should select for this project; it is useful when teammates or tools need a consistent interpreter. A .gitignore can list generated or machine-specific files not worth committing. A README.md can explain what the project does and the command needed to run it. None of these is compulsory for a one-line script.
A test directory holds checks that exercise code in lib. A full project may use a testing gem, but you can start with the simple check below. It loads the same module as the executable, compares actual and expected text, raises an error if they differ, and prints a success message otherwise. This is a learning example, not a replacement for a test suite with useful coverage as the project grows.
test/notes_test.rb — a small executable check
require_relative "../lib/notes"
raise "wrong title" unless Notes.title("Ruby") == "Note: Ruby"
puts "Check passed"Terminal — run the check from the project root
ruby test/notes_test.rbChoose the files your project actually needs
For a first program, keep hello.rb and run it with ruby hello.rb. Move reusable behaviour into lib when the script grows; add a bin entry point if the program becomes a command, tests when behaviour needs checking, and a Gemfile when you introduce external gems. Keep paths and project-specific Ruby versions documented so the next person can run the same code.
RubyMine or VS Code can show this folder as a project, but the folder structure belongs to your program and works from a terminal too. A Rails project may organise files differently because the framework adds its own roles and tools.
Syntax
require_relative "../lib/notes"Examples
Load a local module from a command
Save the two files in the shown folders, then run ruby bin/notes from the project root. Expected output: Note: Ruby.
require_relative "../lib/notes"
puts Notes.title("Ruby")- Save the shown lib/notes.rb module separately, then save this snippet as bin/notes.
- require_relative resolves ../lib/notes from the bin/notes file, so the local module is available.
- Notes.title("Ruby") returns Note: Ruby, and puts prints that result in the terminal.
Best practices
- Start with the smallest layout that makes the code clear.
- Use require_relative for local source files and require for features on the load path.
- Commit an application’s Gemfile and Gemfile.lock together when it uses Bundler.
Runtime & versions
Ruby interpreter. Feature availability depends on the language and runtime version used by your project; consult the official reference below.
Ruby documentation