An app I built for a client over a four-month period never got published.
An app I was working on for a startup ended up combusting into flames and also never got published when I was the one who was supposed to be leading development.
An app I was leading development for in a game development club ended up never getting finished, as I was incapable of properly managing and directing my team.
An app I was developing to self-publish on the App Store ended up getting trashed because I wasn’t happy with the end product.
An app I was planning development for to create my own augmented-reality startup ended up getting pushed to the side because I found out someone else was already building something similar to it — even after I recruited two very talented senior developers to work with me on it.
These are very bittersweet memories I hold within the confines of my psyche. My mind often likes to wander into the past and wonder, “What if it had all worked out?”
What a pleasant thought to traverse. Fortunately, I’m not too worked up about it. I look at where I’m at now and can’t help but appreciate what all of these lost dreams have done in instilling increased value in my present and future.
If it wasn’t for these failures:
- I wouldn’t have noticed where I was lacking.
- I wouldn’t have noticed where I needed to improve.
- I wouldn’t have noticed why I failed.
- I wouldn’t have noticed what it really takes to succeed.
- I wouldn’t have noticed what it really takes to create something substantial.
- I wouldn’t have noticed that it was failure and the pressure to do better that would allow me to become a better developer, leader, and all-around person.
Lesson 1: Perfection Is the Dream-Killer
Ah, perfection. The quintessential idea of what we aim to achieve and in turn romanticize in our minds. It’s that overwhelming desire to achieve the exact outcome that we envision in our minds. We have high hopes that it’ll seize the attention of all those who interact with the final creation.
Nothing has impeded my ability to make forward progress in development more than striving for perfection.
It made me feel like whatever it was I was building out wasn’t good enough.
I wouldn’t celebrate little wins. I wouldn’t focus on atomically approaching tasks. I wouldn’t focus on simplicity. I wouldn’t focus on refactoring. I wouldn’t know where to even start because my mind was way too focused on the big picture rather than the problem I was trying to solve right in front of me.
This isn’t to say that being a big-picture thinker isn’t important at all. The idea you lay out for yourself is the driving motivation behind an entire project after all.
But that doesn’t mean you should delay deployment solely because you aren’t where you want to be quite yet.
Perfection takes time. Perfection is not something you can simply strive for. Rather, perfection is the result of the tiny achievements and improvements you make throughout the entire lifetime of the product you’re developing.
Perfection is seen in the eyes of the user — not the eyes of the creator.
That’s why rather than aiming to be perfect, you should deploy the imperfect and be willing to alter your course as a response to the results you sequentially see from tiny wins. If you can’t adapt, be fluid, and be dynamic in the way you move forward through development, you’re only setting the stage for failure to take the spotlight.
Be willing to be criticized. Be open to the notion that you won’t achieve that perfect idea in your head. Be aware that the imperfect may just be the precursor to perfection that you’ve been looking for.
What’s important is that you’ve developed something more functional and valuable than you did the day before.
Focus on the small wins, and as time goes on, every tiny thing you achieve will evolve together into a fluid and functional application that people will want to use because you focused more on building out the little things very well rather than everything all at once.
Remember that Rome wasn’t built in a day. Develop, refine, simplify, deploy, fail, reiterate, and you’ll eventually build an even better Rome than you imagined.
Lesson 2: Clean Code Is the Precursor to Great Code
Great, seamless, fast, and simple code is something that we all want to create but tend to fall short of. It’s easy to write a piece of code on the first go and tell yourself it’s absolutely sufficient just because it’s functional.
More often than not, it’s less than that.
There’s this horrible saying in programming that I believe sets up budding and even seasoned programmers for much grief and failure: “If it works, don’t touch it.”
This is a philosophy I used to follow in my early years of programming, and guess what? Following this philosophy is a perfect formula for developing disastrous components that are slow, unscalable, and complicated to understand.
I would get halfway through development just to realize that the application I was writing was painfully sluggish and error-prone due to my failure to recognize that the code I initially wrote was crap and needed refactoring into a cleaner, more understandable, and simpler structure.
Messy code may work at first. You might even intuitively think that you’re actually saving time by just moving on from it. Paradoxically, messy code slows down development altogether despite the fact that it was faster to write.
- It makes the code harder to read and understand when you come back to it.
- It’s harder to test.
- It’s tightly coupled and requires regular maintenance and modification.
- It’s hard to extend.
- It’s error-prone.
- It’s hard to reuse, thus influencing duplicate code.
Instead, just take the time to focus on writing clean code. Code that is self-commenting, reusable, simple, short, extendable, modifiable, and scalable.
This is achieved by working towards simplicity, modularity, and decomposition through careful, thoughtful practice and big-picture thinking.
Write your code in the cleanest way possible so that you can save time later.
Lesson 3: Being a Great Programmer Means So Much More Than Just Having Skill
You can be the best programmer in the world and execute with absolute grace and flawlessness. You could be the ten times the programmer we all dream to be.
The problem with that is if you just become a cowboy-type programmer who has complete disregard for the success of their team, you’ll be missing the essential ingredients it requires to bake the cake of success. Rather, you’ll just end up using all your energy to later find that the cake fell flat and burnt to a crisp.
Of course, a huge contributing factor of successful applications is the engineering prowess that put it together, but one must realize that the true dream is achieved through a collective team effort and the processes surrounding development — not a single person.
All of my past failures ultimately met their fate due in part to a simple lack of communication, organization, planning, and cooperation.
A great programmer looks well beyond the craft and extends their abilities by asking the right questions and organizing themselves and their team accordingly through great leadership and not solely through writing great code individually.
This requires a few things:
- Taking the problem you’re aiming to tackle and breaking it apart into micro-tasks that are achievable and unintimidating.
- Communicating those tasks effectively to your teammates and ensuring that they completely understand what they are supposed to be building out.
- Creating realistic deadlines for yourself and others to work towards so that productivity is managed in the most streamlined way possible.
- Operating under a carefully designed programming paradigm and methodology so that everything is built in a structurally sound and consistent manner.
- Reviewing each other’s code to get a different pair of eyes on what you’re working on. This provides team members the chance to supply their valuable advice to you about what you can improve upon and change.
You don’t have to be the leader to do these things either. Simply lead through action and by taking personal initiative. You can’t rely on your manager or team lead to hold your hand and the rest of your team’s hand through the entire process.
Remember that development is a result of collective effort and collective leadership.
Lesson 4: Knowing Everything Is Not Necessary
Sometimes, I wish I was all-knowing. It’s too bad that I never will be, but if I did know everything, what would be the fun in that?
Honestly, what makes programming such a rewarding experience is not only that you’re creating functional things that you know someone will find value in but that there is always something more to learn and that there will always be more room to grow.
The only roadblock you have in this profession is your own mind and creativity — the sky’s the limit.
I didn’t always abide by this philosophy, which is part of the reason why I found myself stagnating and procrastinating so much in the early stages of development.
I was trying to learn absolutely everything I could possibly know about a certain aspect of development before I even started on the project I was initially aiming to build.
I found myself regularly lost in the dark pits of tutorial hell. My desire to be all-knowing and confident in how to build out everything before even starting was paradoxically a form of procrastination that I mistook for productivity.
Instead of actually building what I wanted, I would watch hours and hours of tutorials instead.
I’ve found that it’s better to instead learn as you go, learn by creating, and research through calibrated questions.
Get hands-on with what you’re doing rather than trying to be comprehensive in knowledge. There’s far too much information to know to get lost in that rabbit hole. It’s much better to spend your energy focusing on forward progress.
Lesson 5: Failure Is All Part of the Process
Without failure, we wouldn’t be able to learn. Without code that breaks, we wouldn’t be able to create unshakeable code. Without messy and complicated code, we wouldn’t be able to understand the importance of creating clean and simple code.
It is in failure that we discover ways to succeed by becoming enlightened about the differentiating factors in what works and what doesn’t.
That’s why it’s essential to not only celebrate, repeat, and improve the processes that led to your wins but to also recognize your failures as experiences to learn from and understand what is necessary to avoid and eliminate.
Nothing leads more to failure than thinking that everything you do is the right way of doing things. You do not know everything and there is absolutely no shame in being wrong.
As Albert Einstein once said, “The definition of insanity is doing the same thing over and over again and expecting different results.” Doing the same thing repetitively is a much-too-easy habit loop to fall down, as it doesn’t require new thought or increased effort.
Learn to fail with grace and humility and you’ll find yourself ascending beyond the bounds of success and transcending above the knowledge you once thought you had. It’s hard work, but I can assure you that learning from failures and applying those lessons to your advantage is an effort that’s worth every ounce of your energy.
That being said, I’ll leave you with this brilliant quote from the infamous Stoic teacher, Epictetus:
“If you are defeated once and tell yourself you will overcome, but carry on as before, know in the end you’ll be so ill and weakened that eventually you won’t even notice your mistake and will begin to rationalize your behavior.” — Epictetus, Discourses, 2.18.31