Reflecting on my First 2 Years of Software Engineering

Zheng Hao Tan
5 min readFeb 23, 2018

--

I’ve been working full time at Hologram for 2 years now. This gig happen to also be my first out of college, and I’ve learned quite a few good (painful?) lessons along the way that I would like to share in section below. Let’s get to it, shall we?

Good Engineering Practices Go A Long Way

Finding the right balance between moving fast and still building strong, clean and maintainable code has been challenge. Just ask him:

Click through the tweetstorm

Aim to have better Devops processes. Moore’s Law has brought us engineers great productivity benefits, and if you’ve not been in a cave for too long, I’m sure you probably heard of concepts like continuous integration (CI) and containerization. It may or may not work for you depending on the team and size, but in most cases, they do at some point. Having a simple yet rapid build, merge, test and deploy loop is the best way to ship code fast and reliably.

Aim for smaller + more frequent pull requests! It’s hard to read big ones, making them prone to the LGTMs cause nobody gives a shit as long as the code “looks like it works”. By submitting smaller code changes / deltas, people can narrow down on what the code is actually trying to do. You’ll get better constructive feedback and the reviewer(s) will thank you later for not having to maintain a huge mental context introduced in the pull request.

Don’t check in/version control your API keys in your main application repository. There’s a reason for why empty config files are version controlled. It is a huge security risk. You should be injecting them via your Devops workflow anyway.

Watch for environment specific values when you’re checking in code. Make sure it plays well with all other developers working on the same project. You may have a ~/cute/pony/my/dear/fairy/run-me-if-prod-is-down.sh , but some developers aren’t so cute. Make your application robust to relative paths, so other developers can change those configs on the fly and it’ll just work the same way as it does on yours. Better yet, use something like Docker.

Try to use properly vetted 3rd party packages that have more traction and support. Proper planning and making good software design decisions are key here. One of the things that I learned the hard way back in my early days were to use old PyPI packages that were not well maintained. Heck, they might even be vulnerable! Spending an extra 30 minutes researching and figuring out better packages with an active community can bring huge productivity gains to your project in the long run. It’s way better than being too dependent on one of those packages, and then giving yourself a massive headache over the technical debt introduced if it no longer works for your product/solution.

Clean Code and Proper Abstractions

Writing clean code is an art of itself.

Sure, technical debt isn’t going to sink your product/startup, but it sure is hell for your engineers to be living it every single day.

I’d shorten this post, spare you the boredom and just point you to these books:

  1. Code Complete
  2. The Art of Readable Code

Here’s a quick tweet I made:

alright moving on…

If you haven’t heard of SOLID and DRY principles, go and read them now.

Proper code abstraction makes the codebase:

  1. Easier to navigate.
  2. Easier to understand.
  3. Decreases the risk of bugs being introduced.

Of course, don’t be that abstraction astronaut too. Have due diligence and apply the 80/20 rule.

Engineering + Product + Business Team Synergy

Synergy: The interaction or cooperation of two or more organizations, substances, or other agents to produce a combined effect greater than the sum of their separate effects.

Understanding the Why for the product you’re developing is extremely important for its success. Good software abstraction is a step forward in the right direction, but proper communication and cooperation with the product and business team goes a long way. They are ultimately out in the front lines talking to customers, or working on how to close a deal where technical (engineering) details are being ironed out. Don’t get trapped by the engineer’s disease. Work with them and help them understand the ins and outs of the product.

Tooling, tooling, tooling

Know your way around a UNIX shell

Learn to be comfortable in this environment. Pick up a book or two on this topic. Maybe read a few more here.

grep is your friend. You’ll find this extremely useful for figuring out the first clues on which part of the codebase you’ll need to change based on just the sprint task name alone.

For example, if you have a task that says add permissions check to Devices endpoint, you’ll know some combination of Device source files will require changing.

Another one that I’ve been using recently is called ripgrep, or rg, for faster output.

Have a Python package with a requirements.txt file and a small OCD for having the package names sorted alphabetically? Use sort to solve it for you rather than doing it by hand:

sort requirements.txt -o requirements.txt

Heck, why not give these commands a shorter alias?

Here’s what I do for git as well:

I type g cob <newbranch>, cause that’s shorter than git checkout -b <newbranch>

Learn shortcuts in the tools that you use. If there’s anything positive that comes out of gaming addiction during my teenage years, it has to be the in game shortcuts. I write a lot of code in vim these days, but occasionally pull out a directory/project in Sublime cause the codebase is huge and have enough moving parts. I’m not here to debate on why tool X is better, but I trust you to know what works best for you. Once you’ve used it enough, and with strong muscle memory for shortcuts, you’ll be flying around making quick edits and changes before you even know it. Remember:

Every key that I don’t type is more time spent elsewhere.

Script it up

Write scripts if you know you’re gonna use multiple commands over and over again. In this case, I was running into stale .pyc files affecting the correctness of my software. This was the hack I did, and it worked like a charm:

I’ll just run them every single time. It works pretty well for my use case. I’m not a Python expert, but I’m sure I’ve figured out a better way to do this now that I wrote this down. Think of how many times I won’t have to run all the commands manually during development.

Makefiles, cause make test or make install is a lot faster and easier to remember than python setup.py install; <more commands>; <moarrr commands

.tar.gz, .zip, .gz, .tgz…wait what?? I need gunzip, gzip, zip, tar -cdoi@#!*$!?!! …

How did this become so complicated???

what a mess!

So I tuck this function into my .bashrc config file and type extract <something> when I see one of these tarballs.

Think of how much time you’ve shaved!

Conclusion

One major takeaway I have for the software engineering career track is that it’s one that guarantees lifetime learning. Learn fast and learn how to make yourself learn faster.

In a world where we celebrate our failures, I’d go against the grain to say that failures suck. It’s always nice to be successful, and I choose to win than to lose every single time. However, more importantly, life generally doesn’t have rules/limits on number of attempts you can make. Learning to strike faster and more frequently might just work.

--

--

Zheng Hao Tan

Founder / CEO of Cellulose. Angel investor. ex Lexer (YC S22), Cruise, DriveAI, Hologram.