CLI First: Crafting Software Without a Safety Net
An IDE is a luxury cruise ship; the command line is learning how to swim. Both get you across the water — but only one of them still works when the ship is unavailable. If you only ever compile by clicking a green play button, you do not actually know how your software is constructed, packaged or executed. The day the IDE configuration breaks — or the day you must deploy to a headless cloud server that has never heard of your IDE — you are rendered helpless in direct proportion to how much magic you accepted.
The IDE Dependency Trap
- The magic button fallacy. The play button runs a build you did not write, with environment variables, classpaths and compiler flags you have never seen. Whoever controls those flags controls your software — and currently that is a dialog box three menus deep, not you, and certainly not your version control.
- Bloat and cognitive overhead. A modern IDE indexes millions of lines, consumes gigabytes of RAM, and interrupts a beginner's thought with autocompletion before they have finished forming it. The immediate feedback loop that teaches syntax and structure — type, compile, read the error, understand — gets buried under assistance.
The Anatomy of a Bare-Metal Build
Step away from the GUI and the build reveals itself as a progression of honest, inspectable layers:
Phase 1: Raw tooling — javac and java
# Compile: sources in, class files out (note every flag is visible)
javac -d bin src/com/project/*.java
# Run: the runtime resolves classes from the path YOU state
java -cp bin com.project.Main
Takeaway: the compiler and the runtime are separate machines with an explicit contract between them — generated class files on one side, a classpath to resolve them on the other. Every "cannot find symbol" and ClassNotFoundException you will ever meet is a message about this contract; students who have run these two commands by hand read those errors fluently, and students who haven't, don't.
Phase 2: Universal automators — Make and Rake
# Makefile — a dependency graph: rebuild only what changed
bin/app.jar: $(wildcard src/com/project/*.java)
javac -d bin $^
jar cfe $@ com.project.Main -C bin .
test: bin/app.jar
java -cp bin:lib/junit.jar org.junit.runner.JUnitCore com.project.AllTests
.PHONY: test
# Rakefile — the same discipline, but tasks are Ruby code
task :compile do
sh "javac -d bin #{Dir['src/com/project/*.java'].join(' ')}"
end
task test: :compile do
sh "java -cp bin:lib/junit.jar org.junit.runner.JUnitCore com.project.AllTests"
end
task default: :test
Takeaway: build automation is a cross-language discipline, not an IDE feature. Make (C heritage) expresses a file-dependency graph and rebuilds only what changed; Rake expresses tasks as real code. Either way, compiling, testing and generating documentation become scripted tasks that live in source control — reviewable, diffable and shared — rather than settings trapped in a local IDE workspace.
Phase 3: Ecosystem build engines — Ant, Maven, Gradle
Industrial Java asks two more questions Make cannot answer well: where do third-party libraries come from (transitive dependency management), and how do a thousand projects stay navigable (standard layouts)? Maven answers with convention-over-configuration — a standard directory shape and a declarative pom.xml; Gradle answers with a programmable build language over the same dependency machinery. Both run identically on your laptop and on a CI server, which is exactly the point: an enterprise pipeline cannot click your green button.
Text Editors + the Shell
- Light editors keep you honest. Vim, Emacs, Micro, or VS Code without heavy extensions force you to read your own imports, remember structural syntax, and retain mental ownership of the codebase — the code lives in your head, with the editor as a viewport, rather than living in the IDE's index with your head as a viewport.
- If it runs in a shell script, it automates everywhere. A build you can execute from the terminal is a build a CI/CD server can execute, a container can execute, and a benchmark harness can execute headlessly (see CI/CD). This page is the prerequisite for that one.
The golden rule of tooling: use an IDE when your project is too large to fit entirely in your head. Do not use an IDE because the mechanics of compilation are too confusing to understand. If you cannot build your project with a terminal and a configuration file, you haven't engineered a system — you've built a captive asset of your development environment.