Coming from frontend development, I found complex backends much harder to work in. I could feel the difference, but I couldn’t explain it. Over time, I’ve come to understand what was making them harder for me.
A lot of it comes down to data flow. In the frontends I worked on, behavior was usually relatively localized: someone clicks a button, an API gets called, and something updates on the screen. I had a concrete input and a visible result to follow.
In a backend, an API request might write to a database and enqueue a job. That job starts another input-output sequence, which might trigger another one. The behavior is spread across the system, and understanding the original request means understanding that whole journey.
That puts more pressure on your mental model. You have to identify the different black boxes, understand their inputs and outputs, and remember how they connect. There’s more to hold in your head because the code you’re looking at only explains part of what happens.
Some backends I’ve worked in also hide behavior behind deep inheritance, long call stacks, and abstractions. Figuring out what an endpoint actually does can require digging through several layers. I encountered less of that in the more functional frontends I was used to.
And a seemingly small backend feature can require knowledge of several systems: the database, job queue, instrumentation, analytics. On the frontend, I could often make a useful change by adding a button that called an existing API. In the backend, useful changes often required a broader understanding before I could get anywhere.
Those are the differences I couldn’t name at first: distributed behavior, hidden execution paths, and more systems to understand at once.
In the next article, I’ll describe how I approach building that understanding systematically.