After reflecting on a series of code reviews I’ve done for junior developers I’m mentoring at Microsoft, I’ve distilled some key takeaways about what it takes to write truly robust code.

Writing robust code isn’t just about getting something to work — it’s about creating solutions that are clear, maintainable, and adaptable. The best code doesn’t need to be explained; it speaks for itself through thoughtful naming, simplicity, and adherence to proven standards.

When I mentor, I focus on building these foundational habits, because they’re the cornerstones of long-term success. In every review, I challenge my mentee not just to solve the problem at hand but to write code that’s a pleasure to work with — code that can stand the test of time and scrutiny.

Naming and Readability: Don’t Make Me Think

Code should tell a story, and it should be readable enough that another developer — or you, six months from now — can follow along without needing a PhD in your thought process. This is where naming conventions and method design shine.

In one review, I noticed my mentee had a method named UpdateStorage, which was doing everything from initializing new storage files to overwriting existing ones. I told her, “This name isn’t helping me. When I read UpdateStorage, I think it’s updating something already existing. But this method is doing so much more — like initialization. That’s not updating. Call it what it is: InitializeOrUpdateStorage or even better, split it into two smaller methods.”

Good naming is like good manners in code — it’s invisible when done well, and glaringly obvious when done poorly. The same applies to comments. I’m all for documentation, but commented-out code or comments like “This does X” when the code literally shows it does X? That’s noise, not signal. I’ll gently but firmly tell my mentee, “If it doesn’t add value, delete it.”

Keep It Simple, Stupid (KISS)

Simplicity is a virtue. Complex code isn’t clever — it’s a liability. I drive this home repeatedly: every layer of abstraction or every extra tool should justify its existence. If it’s not solving a real problem, it’s adding one.

For example, when reviewing a test setup, my mentee had added a lot of boilerplate for dependency injection, even though her tests didn’t need it. “Why are you using Dependency Injection here?” I asked. She explained that it was something she’d learned from production code. “That’s great,” I said, “but in a test, you’re in a different world. You don’t have to mimic production if it adds complexity. Just new up the class and pass in the dependencies directly.”

When you start simple, you leave room to grow. It’s harder to scale down from unnecessary complexity than it is to scale up when needed.

Readable Constructors and Thoughtful State Management

A constructor is often the first entry point for someone reading your class. If it’s overloaded with parameters, it can quickly become overwhelming. My rule: put long constructor parameters each on their own line. I reminded her, “This isn’t about saving space. It’s about making the code readable. Someone should be able to glance at this and understand what’s being injected.”

In the same vein, any parameter initialized through the constructor should be marked readonly. “If it’s set once and never changes, make it readonly,” I said. “It’s a subtle signal to anyone reading your code that this state is fixed after construction.”

Paths and Constructors: Keep Things Clear

When it comes to paths in code, I emphasize a simple rule: going up in relative paths is bad, going down is fine. “Every time you go up with ../, you’re introducing fragility into your system,” I explained. “It’s better to work from a known base path and build down from there. Going up just increases the chances of breaking things if someone reorganizes files.”

Constructors, too, should avoid doing too much heavy lifting. If there’s setup involved, I advise breaking that logic into clearly named methods. “Call these methods from your constructor, but make their purpose explicit. For example, prefix them with Initialize. That tells everyone reading your code that this is a one-time setup.”

The Importance of Standards: Gitignore and Usings

When starting a project, one of the easiest wins is using a standard .gitignore file. There’s no need to reinvent the wheel here. Using the tried-and-tested templates from repositories like GitHub’s gitignore repository ensures you avoid committing unnecessary files and keeps your repo clean. I told my mentee, “This isn’t where you show your creativity. Stick with the community’s established patterns.”

The same applies to usings. Keeping them sorted and removing unused references isn’t just about aesthetics — it’s about clarity. “Every unnecessary using is cognitive overhead,” I’ve said. “Sort them, clean them, and let your IDE do the heavy lifting.”

Conclusion

Robust code isn’t an accident — it’s the result of intentional decisions at every level, from naming conventions to architectural simplicity. By focusing on clarity, keeping things simple, and adhering to community standards, we not only improve the quality of our work but also reduce the mental overhead for everyone who comes after us. Whether it’s choosing the right names, simplifying constructors, or sticking to established patterns, these small but meaningful habits make a world of difference. When we prioritize these fundamentals, we’re not just writing code — we’re creating a legacy of craftsmanship and collaboration.