Last week, MIMO launched the KUMA protocol, an Ethereum-based protocol that brings interest-bearing tokens backed by real-world bonds. It was the first DeFi protocols that I’ve played a small role in developing from start to finish. Witnessing all stages - from initial ideas and specs, implementation, security audits, and integrations, has all been awesome experience as a blockchain engineer. As this marks a milestone, I’d like to reflect on what I’ve learned in the past 1.5 years of Solidity development, comparing my initial experiences with Solidity with where I am now as a slightly-more-experienced blockchain-engineer. Also, it might be fun to remind myself how utterly incompetent I was not too long ago.
Oh, and I suppose this might also be useful for people thinking about getting into getting into Solidity development as well. So here are a few ways I’ve improved as both as blockchain engineer, and more broadly as a software developer!
General Engineering Learning
Even though development on the blockchain can be significantly different than other types of development, there are a lot of similarities between Solidity development and software development in other stacks. Reading other people’s code, being part of a larger org, and aligning with everyone towards a single goal are all convergent skills for building™️. The main areas where I’ve grown generally as a software engineer are:
Getting good at reading code. When I first started as a blockchain dev, the majority of my experience was as a solo dev for a small startup. While that was great experience for strengthening my volitional muscles, I didn’t have much experience looking at other people’s code. Thus, I was very slow at reading code. I would try to understand every line of code that I was reading, and that’s obviously not time efficient when there’s a lot to develop and review. Through looking at many PRs in the past 1.5 years, I’ve been able to learn quick ways of reading through code. I now know that it’s not always super necessary to read and understand deeply every single line of code - some lines can be skimmed. Also, breaking things into smaller parts and focusing on understanding one part at a time makes reading a lot of new code much less daunting. Using simple tools like ctrl+find to find how pieces of code are used, or even Github’s “mark as read” functions are small but important ways to make reading code much easier.
Being able to better build on other peoples’ solutions. Reading other people’s code has had the added benefit of exposing myself to others’ ways of thinking. For junior engineers, it’s often easy to solve problems from scratch, and to find a solution to things. However, in irl development, integrating new code with work that’s already been done is quite a useful skill as well, though it is something that can only be developed through repeatedly … building on other people’s code.
Learning to appreciate simplicity. From seeing the many stages of development a piece of code goes through - from the initial review, to integrations, to deploying and any additional bug fixes, I’ve gained an appreciation for writing code as simply as possible. Cutting down on code complexity yields many dividends, and even if it takes longer to write initially, writing code that’s as simple as possible saves SO much time throughout the whole development life cycle.
Being better at writing tests and coming up with edge cases. I used to despise writing tests as it seemed so meticulous. Having to keep track of everything that could go wrong is so much less fun than just building things. However, after being burned repeatedly by bugs which could have easily been avoided through writing more comprehensive tests, I now appreciate writing tests a bit more, and each edge case I’ve been burned on before is now something that automatically comes to mind when writing test suites. Nowadays, instead of just seeing tests as meticulous, I now see tests as meticulous and very important. I sometimes appreciate them for keeping us all safe. At least, when I’m in a patient mood.
Solidity - Specific Growth
Of course, having been around the block once before (pun intended) has also given me some blockchain-specific experience. The main blockchain-specific skills I’ve learned in my 1.5 years of development are:
Knowing Solidity best practices and the less common smart contract exploits. Any Solidity developer regardless of experience level, would sooner work in tradfi than not follow the checks-effects-interactions pattern when sending transactions to external addresses. Though I knew the famous Solidity exploits like reentrancy attacks and tx.origin phishing, there are more niche things I encountered over the past 1.5 years that I now know to always be on the look out for. Things like following best access control practices, being careful when caching variables, validating outputs from external calls, and knowing rounding error bounds, can all minimize unintended behavior and should always be practiced when developing in Solidity. Some of these small slip ups could have led to very big (~ 9-figure) impacts, but luckily all were caught internally and in security audits. That’s why it’s always important to audit your code folks.
Learning how to use more sophisticated testing tools. As important as it is to not rush through writing manual tests, there are still some things that can fall through the cracks. That’s why it’s important to use tools like fuzz and invariant testing, or even smaller checks like adding code coverage to your CI.
Knowing when to make tradeoffs for gas costs. Gas optimizations are a unique dimension to Solidity programming. Knowing how to implement gas optimizations can be quite a useful skill, but it’s even more useful to be mindful of what the trade offs are for gas optimizations. Often times, code that’s more gas efficient is less readable and can even be less secure. This can conflict with the whole point of smart contracts, which are made to be auditable and secure pieces of code. I’ve gotten better at both knowing about the common gas optimizations, and having an intuition for when these are worth implementing.
Familiarity with the Solidity development life cycle. For Solidity code, the life cycle is usually to 1.) Develop initial contracts from a spec (which may or may not require reading up on some financial information), 2.) Review code internally, 3.) Address any review comments (or cycle between 2 and 3 until things are smoothed out), 4.) External security audit, 5.) Aid with any integrations (e.g. from building any frontend or backend integrations). Being familiar with this cycle and having seen it run its course a few times has helped calibrate my estimation abilities for when things will get done, which is always good skill to have. So nowadays I’m only off by a factor of 10x when I try to predict how long things will take!
Thing I’m Working On
Of course, despite these 1.5 years of growth, there’s still a lot of stuff I want to improve on! In the next 1.5 years, I want to be better at:
Being more comfortable with complicated proxy patterns. I’m successfully developed simple proxy or clone contracts, but would like more practice in using Diamond Patterns!
Keeping up with how other projects are building. Best practices in Solidity are ever evolving, and I’d like to get more practice looking at external Solidity code.
Keeping up with the DeFi space as a whole. Knowing what to build is as important to me as how to build it. Since I’ve been in the DeFi space for a bit, I’m hoping to get sensing overall trends on where the space is going.
In a relatively high growth field, it can be easy to get caught up in all that’s happening and to lose track of how much you’ve grown. It’s always fun to look back now and then. I can’t wait another 1.5 years where I find my current skill-set incompetent as compared to where I will be. Until then - Happy Coding!