I’ve been fortunate enough to work on production systems at Meta and IBM, build side projects that real people use, and make plenty of mistakes along the way. Here’s what actually mattered when it came to shipping code that worked.
Early in my career, I’d spend days perfecting a solution before shipping it. At Meta, I learned that iteration beats perfection. Deliver something that works, get feedback, improve it.
During my internship, I delivered over 70 diffs in three months. Not all of them were perfect. But they all moved projects forward. The key was getting to “good enough” quickly, then iterating based on real usage.
Lesson: Don’t let perfect be the enemy of shipped.
At Meta, I found that the Agent Connect UI was making redundant GraphQL queries. It wasn’t a complex algorithmic problem—just multiple components fetching the same data independently.
At IBM, we had report queries taking 45 seconds because of missing database indexes. Again, not rocket science. Just something no one had profiled and measured.
In both cases, the fix was straightforward once we identified the problem. The hard part was actually looking.
Lesson: Profile and measure before you optimize. And then optimize the things that actually matter.
When I built the Exchanger app (a cross-platform currency exchange tool with Flutter), I skipped writing proper tests because “it was just a side project.” Then I changed the API structure and broke the mobile app’s ability to fetch exchange rates.
At IBM, we caught similar bugs in CI/CD before they hit production. SonarQube and automated tests weren’t exciting, but they worked.
Now I write tests not because I’m disciplined, but because debugging production issues at 2 AM is worse than writing tests.
Lesson: Future you will thank present you for writing tests.
Domain-Driven Design felt like overkill when we started the warehouse management system at IBM. All that ceremony around bounded contexts and aggregates seemed excessive for “just a CRUD app.”
A year later, when we were adding new features and integrating with third-party systems, DDD paid off. We could modify one domain without breaking others. New developers could understand the codebase by understanding the business logic, not just following code paths.
Contrast that with projects where I didn’t think about architecture upfront. They worked fine initially, but every new feature became exponentially harder to add.
Lesson: Architecture isn’t about being fancy. It’s about making future changes easier.
At IBM, we set up Azure DevOps pipelines from day one: automated tests, SonarQube analysis, security scans, deployment automation. It felt like extra work at first.
Then we hit a critical deadline. Because we had CI/CD, we could deploy confidently multiple times per day. Teams without it were doing manual deployments and testing, which meant they shipped slower and broke more often.
Lesson: Set up CI/CD early. It’s an investment that pays off immediately.
At Meta, I worked across seven teams while building the Case Audit AI Tool. The code itself wasn’t the hardest part—figuring out what everyone needed and aligning on APIs was harder.
I received recognition not just for the code I wrote, but for how I clarified requirements, documented decisions, and kept everyone in sync.
Good code that solves the wrong problem is worthless. Mediocre code that solves the right problem after talking to users is valuable.
Lesson: Talk to your users (and your teammates) before you write code.
The Raspberry Pi smart glasses project was constrained by power, processing speed, and physical size. Those limitations forced me to:
If I’d built it as a web app without constraints, I probably wouldn’t have optimized as thoughtfully.
Lesson: Constraints aren’t obstacles. They’re forcing functions for better engineering.
I’ve bricked Raspberry Pis, deployed broken code, misjudged API rate limits, and written database queries that brought staging environments to their knees.
Every single one taught me something I remember better than any tutorial or documentation could.
The warehouse system’s performance issues taught me about database indexing. The GraphQL optimization taught me about client-side caching. The smart glasses taught me that hardware is unforgiving.
Lesson: If you’re not occasionally breaking things, you’re not pushing hard enough.
I’ve built production systems with Java/Spring Boot, PHP/Hack, Node.js, Python, and C#. Each team had their reasons for choosing their stack, and you know what? They all worked fine.
What mattered more:
The language or framework is less important than the engineering discipline around it.
Lesson: Master principles, not tools. Tools change; principles don’t.
The Smart Glasses project and Exchanger app didn’t make me money. They probably cost me money in fried components and cloud bills.
But they gave me:
My internships taught me how professional software is built. My side projects taught me how to figure things out when no one’s there to help.
Lesson: Build things outside of work. Even if they’re silly. Especially if they’re silly.
I’m currently pursuing my Master’s in Computer Engineering at UC San Diego, diving deep into machine learning applications, networked services, and distributed systems. Each project and internship has shaped how I think about building software, and I’m excited to see where these lessons take me next.
If you’re early in your career, here’s my advice: ship things, break things, fix things, and write about it. The industry needs fewer people who know frameworks and more people who know how to solve problems.
Background: Software Engineer & Grad Student at UC San Diego
Experience: Meta, IBM, and various side projects
Reach out: ryankuang0618@gmail.com