One year in: what production code taught me that tutorials didn't
Notes from twelve months of shipping code that actual customers run, after two years of tutorials, courses and personal projects.
I spent two years learning to code before anyone paid me to do it. Tutorials, side projects, a bootcamp, an internship. I thought I understood what the job was. About a year into my first real role, I can say plainly: most of what I needed to know wasn't in any of that.
None of this is a critique of tutorials. They got me here. But there's a category of things you only learn by being trusted with code other people depend on, and a lot of it is unglamorous enough that it doesn't make for good content. So I wrote it down.
Reading comes before writing
Tutorials are 95% writing. Production is 80% reading. Reading the ticket, reading the existing module, reading the migration history, reading the diff someone else opened yesterday. The first time you're handed a codebase with a year of decisions in it, you realize that writing the change is the easy part. The hard part is knowing where it goes and what it touches.
I changed how I work because of this. When I pick up a ticket now, I don't open my editor first. I open the part of the app the user actually sees, click around as them, then trace one full request from the route to the database. By the time I'm typing, I know what I'm doing.
Consistency over taste
Every codebase has three or four patterns for the same thing. Three ways to fetch data. Two validation styles. A controller that looks one way in the old domain and another in the new one. Tutorials never show you this. They have one author and one opinion.
The instinct is to introduce a fifth pattern, the right one. The lesson is that adding another way to do it costs the next person more than living with a pattern you'd have chosen differently. I keep a rule for myself now: match the file I'm in, match the folder if I'm new to it, and only propose a new pattern in a separate change with a reason attached.
The cost of a wrong assumption found in week two of a feature is much higher than the cost of a fifteen-minute conversation in week one.
Ten minutes of context
I used to read for an hour before asking anyone anything, because I didn't want to look like I hadn't tried. What I learned is that the cost of a wrong assumption found in week two of a feature is much higher than the cost of a fifteen-minute conversation in week one. Senior people are not annoyed by good questions. They're annoyed by questions that could have been answered by reading the README, which is a different problem.
Now I read first, write down what I think the answer is, and then ask to confirm or correct it. That format gets quick answers and doesn't waste anyone's time, including mine.
Errors live in the codebase, not the language
On one of my early projects I inherited a Laravel app with around 3,800 static analysis errors. Most of them were not because someone didn't know PHP. They were because the codebase had grown faster than its types, faster than its conventions, faster than the team's memory of why a function returned what it returned.
Bringing it to zero wasn't a typing exercise. It was an excavation. Every error was a small archeological dig into a decision someone had made under pressure two years ago. Tutorials teach you the language. Production teaches you how decisions accumulate, how they decay, and how to clean up after them without breaking anything alive.
You inherit other people's trade-offs
On a healthcare project I worked on, the calendar system had to handle Multibanco payments, a payment method that only exists in Portugal and has its own delays and reconciliation rules. None of this is in a tutorial. None of it is in the framework docs. It exists because the country exists, and because the team before me made calls about how to model it.
Some of those calls I would have made differently. Most of them I now understand the reasoning for. The job isn't to relitigate every choice. It's to know which ones are still costing you, which ones are paid off, and which ones you'd undo if you had another week. That's a kind of judgment that doesn't compress into a blog post.
Shipping isn't deploying
Merging a pull request and a user actually using the feature are not the same event. Between them sit: feature flags, a slow rollout, a customer-success person who needs to know what's about to change, a support article that has to be updated, a few users who will hit the new code path before anyone else and tell you something you didn't anticipate.
Tutorials end at "the app runs." Production starts there. The first time you watch a feature you wrote get used by someone whose afternoon depends on it, the relationship between you and the code changes. You stop being clever. You start being careful.
The thing that surprised me most
It's not that production code is harder than tutorial code. It isn't, most of the time. What surprised me is how much of the job is everything around the code: the asking, the reading, the choosing what not to build, the explaining, the second-guessing a decision and writing it down anyway because you'd rather be corrected than wrong.
One year in, I think the skill I'm trying hardest to build isn't a technical one. It's the discipline to stop typing and look around, at the codebase, at the user, at the team, long enough that what I write afterwards actually fits.