How to understand any codebase

Dom Habersack
Dom HabersackApril 1, 2020

I used to freak out whenever I joined a new client. To be productive fast, I have to figure out how their sometimes massive codebase works in a few days. It’s a humbling situation, but one we can speed up with a neat trick.

Whenever I have to learn a new codebase, I start by writing tests for it. They help me see how small units of the project work. Where I begin doesn’t matter all that much. There is no “wrong” place to start from, as any test teaches me something useful.

Functions with input and output are easy starting points. If I know what parameters a function takes, I don’t even need to know what it does before writing tests. Let’s test an imaginary slugify function:

it('slugifies', () => {
  expect(slugify('Hello Code')).toBe(5);

It is possible but unlikely that this function would return a number. I want my test to fail at this stage, because that tells me what the function really returns:

+> expected: 5
-> actual:   'hello-code'

Nice! So this call returns 'hello-code' instead. I can now try another input:

it('slugifies', () => {
  expect(slugify('Hello Code')).toBe('hello-code');
  expect(slugify('… what?')).toBe('…-what?');

Whether this second expectation passes or fails, I will learn something new about the code. I will know what the function does, even though I still have no idea how it does it. Once I have tests in place, I can change the function as much as I want. If my tests still pass, I know I didn’t break anything.

Next time you’re joining an existing project and don’t know where to start, try writing a few tests.

– Dom

Continue reading

March 25, 2020

How to write legacy code

No code starts out as unmaintainable legacy we don’t dare to change. All codebases end up there because of a lot of small decisions like these examples.

Read full article

Read all articles →