What I Look for in the First Hour of a New Codebase
After parachuting into dozens of client projects, I've developed a mental checklist for the first sixty minutes. Most of what matters isn't in the code itself.
The first hour with a new codebase is the most honest hour you'll get. Everything after that, you start adjusting. You learn the workarounds. You absorb the team's rationalizations for why things are the way they are. But in that first hour, your instincts are still calibrated to what's normal, and the gaps jump out.
I've been doing consulting engagements long enough that this initial assessment has turned into a routine. Not a formal checklist — more like a set of questions I let the codebase answer for me.
The README is a litmus test
Not for its contents. For its existence and accuracy.
If npm install && npm run dev (or whatever the equivalent is) gets me a running application within five minutes, I relax a little. If the README references a Docker Compose setup that no longer exists, or environment variables that aren't in .env.example, or a setup script that errors out on line 3, that tells me something about how the team communicates. Documentation that rots means nobody onboards often enough to notice. Or worse, they do notice and don't fix it.
One client had a 200-line README that was mostly accurate — except for the database setup section, which referenced a PostgreSQL 12 container that had been replaced by a managed RDS instance eight months earlier. The engineer who showed me around said "oh yeah, just ignore that part." Three people had onboarded since the change.
What the dependency list tells you
I always open package.json (or go.mod, or requirements.txt) before I open any source file. The dependency list is a compressed history of the project's decisions.
Things I'm looking for: Are there two libraries that do the same thing? That usually means a migration that stalled halfway. Is there a framework-level dependency that's three major versions behind? That's either fine or terrifying, and the answer is usually in the changelog. Are there dependencies that suggest architectural patterns the team might not realize they've committed to — an ORM, a message queue client, a caching library?
The ratio of dependencies to application code matters too. I worked on a Node.js project last year where node_modules contained 1,400 packages for an API that had 26 endpoints. Half of those packages came from a single PDF generation library that the team used for one feature. Nobody had considered that trade-off.
Run the tests before reading the code
This one surprises people. I don't read the source code first. I run the test suite.
What I learn from this is enormous. How long do the tests take? Are they flaky? Do they actually pass? Is there a clear separation between unit and integration tests, or is it all one blob that takes nine minutes and needs a running database?
If the tests pass quickly and cleanly, I know the team has standards. If they fail on a fresh checkout, I know the team has gotten used to red builds. I once joined a project where 14 out of 340 tests were failing, and the tech lead said they'd been failing "for a while." Nobody could remember what "a while" meant. It turned out to be four months.
Note
The git log is a narrative
git log --oneline -30 gives me the last thirty commits, and the story they tell varies wildly.
Clean, descriptive commit messages with consistent formatting mean the team has code review norms. A string of "fix," "wip," "asdf," and "please work" means someone's merging without review, or the review process is a rubber stamp.
I also look at who is committing where. If one person has touched every file in the last month, that's a bus factor problem. If nobody has touched a particular directory in six months, it's either stable or abandoned — and the distinction matters a lot.
Merge commit patterns are revealing too. A long chain of merge commits from the same branch tells me about a feature branch that lived too long. Multiple merge-conflict resolutions in quick succession suggest a branching strategy that's creating more friction than it prevents.
The deployment question
I always ask: "Walk me through what happens when you merge to main." The answer falls into three categories.
Some teams can describe a clear pipeline: merge triggers CI, CI runs tests, passes go to staging, staging gets a smoke test, production gets a blue-green deploy. These teams have thought about it.
Some teams give me a long pause followed by "I think Jenkins picks it up?" Those teams have a pipeline, but nobody fully understands it, and the person who set it up probably left.
The third category is the scariest: "Oh, Dave does the deploys." Dave is a single point of failure wearing a human suit. Dave's vacation is a deployment freeze that nobody calls a deployment freeze.
What the first hour can't tell you
All of this gives me the shape of the project, but it doesn't tell me about the team's actual problems. The messy parts of the codebase might be messy for good reasons — time pressure, pivoting requirements, a former team member's experimental phase. The clean parts might be clean because nobody touches them.
The first hour tells me what questions to ask in the second hour. And those questions — about the decisions behind the architecture, the incidents that shaped the error handling, the product pressures that left certain corners rough — those are where the real understanding starts.
I've made the mistake of drawing conclusions too quickly from a first impression. A project that looks chaotic might be shipping reliably. A project with beautiful code might be six months behind on features. The codebase is evidence, not verdict.
But if the tests don't pass on a fresh checkout? That one's usually exactly what it looks like.