3 Navigating and Understanding a Codebase Quickly
There’s a moment every developer knows. You’ve just cloned a new repository — or been handed ownership of a project someone else wrote — and you’re staring at a directory listing trying to figure out where anything is. Where’s the entry point? Where do the tests live? What even is this scripts/ folder?
Most developers handle this by opening the project in their IDE and clicking around. That works. But the terminal gives you something the IDE file tree doesn’t: the ability to ask precise questions about a codebase and get immediate, scriptable answers.
This chapter is about building that orientation instinct at the command line — getting from “I have no idea what this project looks like” to “I understand its structure” as fast as possible.
3.1 Getting the Lay of the Land with tree
The first thing you want when encountering a new codebase is a structural overview. The ls command will show you the contents of the current directory, but it’s tree that gives you the full picture at a glance.
tree -L 2The -L 2 flag limits the output to two levels deep — enough to understand the top-level organization without drowning in every nested file. A typical output might look like this:
.
|-- src
| |-- components
| |-- services
| |-- utils
| `-- index.ts
|-- tests
| |-- unit
| `-- integration
|-- scripts
|-- package.json
|-- tsconfig.json
`-- README.md
In ten lines you’ve learned more about this project’s structure than five minutes of IDE clicking would have told you. You can see it’s a TypeScript project, it separates unit and integration tests, and there’s a scripts/ directory worth investigating.
To go deeper on a specific directory:
tree src/servicesTo exclude directories that are usually noise — node_modules, build output, .git:
tree -L 3 -I "node_modules|dist|.git"The -I flag takes a pipe-separated pattern of names to ignore. This is one you’ll use almost every time in a JavaScript or Python project.
If tree isn’t installed on your system, you can get it with brew install tree on macOS or apt install tree on Ubuntu. It’s worth adding to your standard developer setup.
3.2 Targeted File Listing with ls
tree gives you the overview. ls gives you the details of a specific location — and with the right flags, it tells you quite a lot.
The version of ls most developers should be using day-to-day:
ls -lahBreaking that down: - -l — long format, showing permissions, owner, size, and modification date - -a — show hidden files (dotfiles like .env, .gitignore, .eslintrc) - -h — human-readable file sizes (4.2K instead of 4302)
The modification date column is particularly useful when you’re trying to understand what changed recently in a project:
ls -lahtAdding -t sorts by modification time, newest first. Run this in a directory and you immediately see what’s been touched most recently — useful for understanding where active development is happening, or for finding a file you edited an hour ago but can’t remember the name of.
To list only directories:
ls -d */And to see the contents of a directory without changing into it:
ls -lah src/services/3.3 Finding Files with find
tree and ls are great for browsing. But when you know what you’re looking for and just need to locate it, find is the right tool.
3.3.1 Finding by name
find . -name "*.config.js"This searches from the current directory (.) recursively for any file matching the pattern *.config.js. The quotes are important — without them, the shell may expand the glob before find sees it.
Case-insensitive search:
find . -iname "readme*"3.3.2 Finding by type
To find only directories named tests:
find . -type d -name "tests"To find only files (not directories) with a .env extension:
find . -type f -name ".env*"3.3.3 Finding by modification time
This is where find starts to pull away from anything an IDE file tree can do. To find files modified in the last 24 hours:
find . -mtime -1Files modified more than 7 days ago:
find . -mtime +7Files modified between 2 and 5 days ago:
find . -mtime +2 -mtime -5This is invaluable when you’re debugging a production issue and need to answer the question: what changed recently?
3.3.4 Excluding noisy directories
Like tree, find will happily descend into node_modules unless you tell it not to:
find . -name "*.ts" -not -path "*/node_modules/*" -not -path "*/dist/*"3.3.5 Combining find with actions
find becomes dramatically more powerful when you pair it with -exec to run a command on each result:
find . -name "*.log" -exec rm {} \;This finds every .log file and deletes it. The {} is a placeholder for the filename, and \; ends the -exec expression.
A safer pattern when you’re not sure what you’re about to delete — preview first:
find . -name "*.log" # see what would be affected
find . -name "*.log" -exec rm {} \; # then actWe’ll return to find and -exec in later chapters when we cover batch operations. For now, the key mental model is: find locates; -exec acts.
3.4 Understanding Files Before Opening Them
Before you open a file, two tools can tell you a lot about what you’re dealing with.
3.4.1 file — what kind of file is this?
file some-binarysome-binary: ELF 64-bit LSB executable, x86-64, dynamically linked
file mystery-datamystery-data: gzip compressed data, was "backup.tar", last modified: Mon Feb 12 09:14:22 2024
This is particularly useful when you encounter files without extensions, or when a file isn’t behaving the way you expect. Before spending ten minutes trying to open something, a quick file check tells you exactly what you’re dealing with.
3.4.2 stat — metadata about a file
stat src/index.ts File: src/index.ts
Size: 3421 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 2894732 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ alice) Gid: ( 1000/ alice)
Access: 2024-03-12 09:14:22.000000000 +0000
Modify: 2024-03-11 17:43:09.000000000 +0000
Change: 2024-03-11 17:43:09.000000000 +0000
The three timestamps at the bottom are often what you’re after: when the file was last accessed, when its content was last modified, and when its metadata last changed. This is more precise than what ls shows, and it’s scriptable — you can extract individual fields:
stat -c "%n %y" src/*.tsThis prints the name and last-modified time for every TypeScript file in src/ — useful for a quick audit of recently touched files.
3.5 Putting It Together: A Real Workflow
Here’s how these tools work together in practice. Imagine you’ve just been asked to investigate a bug in an unfamiliar service. Your first five minutes at the terminal might look like this:
# Get the overall structure
tree -L 2 -I "node_modules|dist|.git"
# Understand what's been touched recently
ls -laht src/
# Find the entry point
find . -name "index.*" -not -path "*/node_modules/*"
# Find any config files that might affect behavior
find . -name "*.config.*" -not -path "*/node_modules/*"
# Check if there are any files that changed in the last day
find . -mtime -1 -not -path "*/node_modules/*" -not -path "*/.git/*"In under two minutes, without opening a single file, you have a clear picture of the project’s structure, what it’s built with, and what changed recently. That’s a foundation you can investigate from.
3.6 Modern Alternatives Worth Knowing
The tools covered in this chapter are available on virtually every Unix system. But a few modern alternatives are worth knowing about, particularly if you’re setting up a new development machine.
eza (formerly exa) is a modern replacement for ls, written in Rust. It has better defaults, color-coded output by file type, built-in Git status indicators next to each file, and a tree view mode that combines what ls and tree do separately:
eza --tree --level=2 --git-ignorebroot is an interactive tree navigator that lets you browse, search, and open files all from a single interface. It’s particularly good for very large codebases where tree output becomes overwhelming.
fd is a modern alternative to find with a simpler syntax, faster performance, and .gitignore awareness by default:
fd "*.config.js" # equivalent to: find . -name "*.config.js"
fd -t d tests # find directories named "tests"
fd --changed-within 1d # files modified in the last dayIf you’re working on a new machine, fd and eza are both worth installing. That said, find and ls are available everywhere — on production servers, in CI environments, in Docker containers — and fluency with them is non-negotiable. Learn the classics first, then layer in the modern tools where they help.
3.7 Chapter Summary
The tools in this chapter — tree, ls, find, file, and stat — are your orientation layer. They answer the question what is this codebase? before you ever open a file. Used together, they can give you a remarkably complete picture of an unfamiliar project in just a few minutes.
The key habits to build:
- Start every new project or investigation with
tree -L 2 -I "node_modules|dist|.git" - Use
ls -lahtto see what’s been recently modified in a directory - Use
findwith-mtimewhen you need to know what changed and when - Check
filebefore assuming you know what an unfamiliar file contains - Build
findcommands incrementally — preview before you act
3.8 Exercises
1. Clone any open source project you haven’t worked with before. Use tree, ls, and find to answer these questions without opening any file in an editor: What language is it written in? Where do the tests live? Are there any hidden configuration files in the root?
2. Find all files in a project that were modified in the last 3 days, excluding node_modules (it it’s a node project) and .git.
3. Use find with -exec to list the line count (using wc -l) of every .js file in a project.
4. Install eza and compare its output to ls -lah on the same directory. Which information does each show that the other doesn’t?
3.9 Quick Reference
| Command | What it does |
|---|---|
tree -L 2 -I "node_modules\|dist" |
Project structure, 2 levels deep, ignoring noise |
ls -laht |
Directory listing sorted by modification time |
find . -name "*.ts" |
Find all TypeScript files recursively |
find . -mtime -1 |
Files modified in the last 24 hours |
find . -type d -name "tests" |
Find directories named “tests” |
find . -name "*.log" -exec rm {} \; |
Find and delete all log files |
file mystery-file |
Identify what kind of file something is |
stat src/index.ts |
Full metadata including timestamps |