What it means to be a Senior Software Engineer


What makes a great senior software engineer? That’s the question I always ask managers and senior engineers when I meet with them.

This post discusses the traits of the excellent engineers I have had the opportunity to work with over the years.

1. Empathy

Simple: do not be a brilliant jerk. No one enjoys working with one.

Your teammates are human and have feelings – do not hurt them. No matter how brilliant you are; if you are a jerk, people will avoid interacting with you. Eventually, you will become the ‘brilliant jerk (BJ)’, the ‘respected’ guy on the team that no one wants to work with.

This means being open to opposing ideas – seek to understand what people say and see their viewpoints. Even if you disagree, you can state your thoughts in a respectful way; there is no need to belittle or point fingers.

You do not need to ‘win’ every argument, conserve your energy and emotional strength for more important matters. The trick is to find the core set of high-impact important issues to concentrate on, these are the ones you put your foot down and fight strongly for.

2. See the end to end picture

Farmer: I see a lot of trees

Pilot: I see the Amazon

Astronaut: I see South America

Seeing the big picture is a criteria that distinguishes junior engineers from seniors. As a junior, it is easy to become immersed in implementing some small piece without seeing the big picture. A senior engineer sees how that piece fits into the system, the business need and extension areas.

See the continent, the forest and the trees!

As you go up, do not blindly start coding gung-ho; such approaches will cost you money, time and effort (fixing bugs, reverting code, redesigns). Seek first to understand the full story and see the big picture.

Discuss to get more information about usage, corner cases and expected behaviour. The outcome is better-designed software that is resilient to bugs and easy to extend.

Spend some time thinking about the trade-off between risks and benefits. For tightly-coupled code, a small refactor can trigger an avalanche of downstream changes or break some remote component. This also applies to jumping on the latest fad without fully understanding its design roots; you might not even need that hadoop system.

Senior engineers try to isolate risk and keep the system always running. It is not just about shipping features – it is about shipping features optimally.

3. Crisp Clear Communication

Have you heard of any brilliant scientist that could not communicate?

Concise clear communication is an important skill that most engineers unfortunately lack. Software engineers spend a lot of time discussing designs, explaining architectural decisions and convincing people. They have to go to meetings, attend service reviews and send emails. A lack of good communication skills makes it difficult to get ideas across.

Senior engineers tailor their communication to the target audience. They use the vocabulary and terms to get their ideas across and can express their thoughts to disparate groups. Thus, designers, program managers and their peers understand them.

The good news is that you can learn how to communicate. A heuristic follows:

  1. Know the desired communication outcome
  2. Gather your thoughts
  3. Create a logical sequence for articulating your thoughts
  4. Express your thoughts using the sequence in 3
  5. Know when to stop – no point trying to convince someone who has already closed off their mind
  6. Practice, practice, practice

Learn to get your ideas across convincingly and persuasively – it is key!

4. Sangfroid

Sangfroid: self-possession or imperturbability especially under strain

I met with a high up director when I was about to join PowerBI in 2015. He told me that great engineers have the confidence to take some risk.

Everyone likes a confident person! A confident engineer can motivate teams and colleagues toward achieving a goal. Even if they end up in unfamiliar terrain, past experiences and challenges will have equipped them with the confidence to pull through.

This year, I was working on some critical core of our SaaS offering and no matter how hard I tried some mistakes always slipped through. I was a bit demoralized after one incident and asked A, a senior engineer with nearly 20 years of experience. His advice was thus:

Humans make mistakes and that’s normal, that’s how you learn. What’s bad is making the same mistake twice and not learning from it.

That made my day and I got to realize the difference between learning and building confidence. Do not be too worried about mistakes, rather be ready and fortified to handle them when they arise. That’s sangfroid…

5. Disciplined to know and choose what matters

Time is limited but the amount of work that needs to be done is not. How do you prioritize and deliver the things with optimal impact? What matters to the business is delivering high-quality high-impact outcomes consistently over time.

Choose the tasks with disproportionate impact and do them done first. If your web service is down, what matters most is mitigating issues and getting it back up; deep fixes and reviews can come immediately afterwards. This also applies to working on features that have little business impact – really? If something doesn’t matter, is it worth it investing a lot into that?

A senior engineer approaches boring tasks (e.g., unit tests and documentation) with the same level of seriousness as writing code. These tasks, which are multipliers of value, are just as important (if not even more important) in the long run.

I set up a continuous delivery system via git hooks in 2013 for my team. Pushing to the ‘prod’ branch would update the Amazon Web Service VM we had running in about 2 minutes or so. The naïve me did not write a single line of documentation; I felt it was not needed for three reasons:

  1. I had it all in memory
  2. I could always Google for it
  3. I was the only one handling administration and automation duties

As expected, I got confused after some time trying to understand how the system was set up. That was something that documentation would have helped fix.

Some activities are value multipliers – they save you time and effort. They also make it easier to bring on new developers.

Whatever you do, choose wisely and then do it well!

Conclusion

When can you say you are senior? I will give a few heuristics:

  • When you can lead a huge development project end to end and work across multiple teams to achieve your goal.
  • When you can work independently
  • When you are known as an expert and your views reveal deep wisdom, tact and insight.
  • When you can communicate clearly and express your views

Oh, there are a few other aspects I didn’t cover such as estimation and having the right computer science basics.

Thoughts?

Related

The related posts below show the evolution of my views over the years and make for interesting reading.

  1. Maturing as a software engineer
  2. Advice for aspiring programmers
  3. 10 years of programming: Lessons Learnt
  4. The Effective Programmer – 3 tips to maximize impact
  5. Becoming a Professional Programmer
  6. So you want to become a better programmer
  7. Levels of Developer Expertise
  8. Become a better programmer
Advertisements

New beginnings : New frontiers


I have been pretty much a JavaScript person mostly for the past four (or is it 5?) years – well ever since I did my internship in 2012. No doubt I really like the language, the ecosystem and the potentials. It’s easy to get so engrossed in the ecosystem – there is never a dearth of things to learn or tools to try out. Quite intellectually stimulating and mind-broadening (provided you can spend the time to learn it well).

JavaScript still looks exciting especially with the upcoming changes (async, await, fetch, ES6). As they say however, the more things change the more they remain the same eh? Personally, I think it is time to check out what happens on the other side – the backend. Advocates say server-side development is ‘easier’ and more stable (yeah, they don’t have 1000 frameworks, build tools, task runners and patterns!).

So why the change? Simple answer: Growth. I want to try something new, expose myself to stimulating challenges and stretch myself. What’s the point of finding cozy places? The goal is to grow, expand and become better. And did I just get these thoughts? No, been on my mind for nearly a year now.

So no more JavaScript? Nope – I enjoy that too much and I still have to finish myangular implementation and descrambler. Nevertheless, I am planning to do more full stack work inshaaha Allaah – expect new topics covering micro-services, scaling huge services, rapid deployment in addition to the staples of programming languages, computer science theory and software engineering.

Let’s go!

The difficult parts of software development


Time for a classic rant again; yeah it’s always good to express thoughts and hear about the feelings of others – a good way to learn.

Lots of people think the most difficult aspects of software development revolve around engineering themes like:

  • Writing elegant pieces of code that are a joy to extend and scale beautifully
  • Crafting brilliant algorithms that can land rockets on small floating platforms (yup, SpaceX, I see you…)
  • Inventing new cryptographic systems (please don’t attempt this at home…)
  • Building and maintaining massively parallel computation systems

Agreed, these are extremely challenging and sometimes it is difficult to find a perfect solution. However, since most of these deal with code and systems, the required skills can be learned and there are usually workarounds available.

There is more to software development than engineering and these other facets can spawn tougher (or even impossible-to-solve) challenges.

Here are three of those difficult areas:

1. Exponential Chaos

The combinatorial complexity of code grows exponentially. It’s well nigh impossible and also futile trying to exercise all possible execution paths of a program. Equivalence class partitioning helps a lot to cut down the test space but invariably, we still miss out on a few.

A single if statement with one condition has two paths – the condition is either true or false. Let’s assign the simple one-condition if check code above a theoretical complexity  value of 1. If that if statement is nested in another if statement, the number of paths explode to 4; ditto for two conditions in the if condition check. Re-using our complexity model, this comes to a value of 2 or so.

Most codebases have loads of conditional branching, loops, multi-threading, various components and what have you. So we can safely our complexity values for such code bases in in the high millions or thereabout. Scary? Not yet.

Now imagine what happens when there are hundreds of developers working in that same codebase and making a few hundred check-ins daily? Maybe the complexity value should sky-rocket to the high billions? Trillions?

Given the rapid rate of change and inherent complexity, how do you ensure that quality is maintained? How do you enforce consistency across large groups? A very difficult problem to solve – there are approaches to mitigate the risk but I do not know of any foolproof method that works all the time. If you know of a way, do let me know.

2. I’ll know what I want when I see it

We all think we know what we want – alas, we typically don’t until we see the finished product. Let’s take the following series of interactions between Ade who wants a new dress and his tailor.

Ade: I want some beautiful dress that fits me, is wearable all year round and casual. I want it in 2 weeks.

Tailor: Aha, so you want something casual that fits you, is wearable all year round and need it in 2 weeks.

Ade: Yup right on point

2 weeks later

Tailor: Here it is! (Beaming proudly)

Ade: (Not impressed); I like the fabric and design. But I don’t like the colour, the sleeve length and it doesn’t fit me quite right. Can you change it to black?

Tailor: here, it is in black

Ade: On second thoughts, black would be too hot, could you change it to brown?

Tailor: here it is in brown

Ade: Great! Could the sleeves be shortened by 2cm?

Tailor: Done

Ade: Hhmm, could you revert the sleeves to their original length? I think I now like the earlier design.

Tailor: Done!! (getting annoyed probably)

Ade: Great! This is becoming awesome, could you please reduce the width of the dress? It’s too wide…

Tailor: @#$@#$@$#!!!

Most people usually don’t have physical products tailor-made to their desires. We go to the store (to meet a car dealer, a tailor or an architect) and choose one of the several options there. We can take a car out for a ride, walk through a building or try on a new dress. This helps a lot as we know if we want that product or not.

In software development, it’s a different story – we want something tailored but we cannot express that need accurately until we see the product. Invariably, our descriptions do not match what we desire. To  restate: it’s highly probable that you wouldn’t like a dress you described in its entirety to a tailor when you try it on.

Figuring out what users actually want is a difficult problem – probably why agile methodologies are popular. Less difficult way? Do the minimum possible thing and allow users to play with it. For example, the tailor could have given Ade a paper dress to evaluate all the styles and all that.

Let’s play a simple game: when you start your next project, make sure you document all user requests, also record every update as you go along. I am pretty sure the new requests will significantly differ from the original one. The end product might be totally different from the initial ask even.

3. No laws – it’s the wild wild west out there

If I release my grip on an apple, I expect it to fall down – why? Gravity of course. Most interactions in the physical world are bound by these models. Imagine that a car manufacturer has to design a new super car for some super-rich guy. Mr-rich-guy asks for the following:

  • Must be drive-able by adults, teenagers and infants
  • Must work on Earth, Venus and Mars
  • Can run perfectly on gas, water or coal

The manufacturer can tell him it’s impossible since the current physical models make it extremely difficult to achieve the three impossible orthogonal requirements; maybe if he wants a movie though…

Let’s go to the world of software; consider the typical AAA game, to capture the largest possible market share, products have to be usable on:

  • Multiple consoles (XBox, PlayStation, Nintendo etc)
  • Other architectures (e.g. 32-bit and 64-bit PCs)
  • Operating systems – Windows, Linux
  • Various frame rates

There are also limitations in software (hardware limits, processors, memory etc) but we often have to build ‘cars’ that can be driven well by people in various age groups living in multiple planets!

The support matrix explodes all the time and generating optimal experiences is an extremely difficult task. In fact, most times, the workaround is to have a limited set of supported platforms.

Alas, it’s the realm of 0s and 1s, so software engineers have to conjure all sort of tricks and contortions to make things work. Maybe some day, cars would run everywhere too…

Conclusion

So apart from the technical challenges, there are a few other just-as-challenging (or even more challenging areas) in software development. These include

  • Ensuring your requirements match hidden customer desires
  • Working to meet various regulations and ensuring proper support across platforms
  • Managing technical debt and reducing risk in heavily changed code bases

Your thoughts?

Maturing as a software engineer


Looking back on my time as a developer, there are a lot of things I would have avoided doing if I had as much knowledge and maturity as I did now.

While I am grateful for the experiences and don’t regret them; I felt it would be a good idea to share these. These might motivate others or at least speed up their careers.

Here goes!

1. Patterns, patterns, patterns

When I take part in code reviews, I tend to look for recurring style patterns. Why? This helps to reduce the cognitive load on readers of the code (after all, code is written to be read).

I am  not advocating for bad software patterns rather having a plethora of ways for doing the same thing in a codebase creates confusion and productivity losses. How do you determine the ‘right’ pattern?

For example in JavaScript, there are several ways for creating an array.


var a = [];

var a = new Array();

var a = new Array(3);

Having a haphazard mixture only takes away brain processing cycles. Rather, have your team decide on a style and stick to it.

By the way, the first style is the ‘expected’ and preferred approach although there might be use cases for the latter two.

Ever wonder why the Google codebase is rated to be easy to work with? Well, think about consistency and established patterns.

2. Break the big picture down and make incremental progress

Building and distributing the smallest software piece you can imagine requires more effort than you would think. It is much more efficient to break down the big picture into small chunks of work that can be completed in an hour or less. Such breakdowns make you more effective and help in understanding progress and forecasting completion times (which is a tricky problem to solve).

I used to break down only the code pieces before (which itself was an improvement over my earlier dive-into-code-and-figure-it-out-as-you-go approach). Nowadays, I try to take some time and reflect on the end product itself: its behaviour, look and feel and how users would interact with it.

For a typical software project, such road maps covers:

  • Testing – unit tests, continuous integration,
  • Documentation – extensibility guides, tooling
  • Implementation
  • Discoverability and Distribution – release targets, getting started articles
  • Maintenance – handling bugs, user feedback etc

Sounds like too much work? Well, just focus on one small bit at a time and keep making progress.

3. Be lazy – start first on tasks with the largest impact/effort ratios

Two things matter: results and impact. There is no point in slaving for 20 hours to choose between blue and light blue if it has no impact on the users. Ditto for spending endless hours ‘arguing’ over what language should be used. Just choose the best usable one and deliver results.

My heuristic for tasks is thus:

  • Does completing the task move me closer towards the big picture?
  • Is this the easiest-to-achieve task with the biggest impact?

If so, I pick up that task and just do it – the goal is to maximize the impact/effort ratio.

Before I’d just stick to a task and spend endless hours on it even if it was something as trivial (and probably low-impact) as beautifying test scaffolding test output and elaborately designing test functions. Now? Common, my time is more valuable than that – I get the test functions right and try to get the coverage I want but won’t spend too much time once that is achieved and is readable for others.

Excessive polishing time can be spent on other more impactful pursuits like having fun with family or delivering high impact features.

4. Technical skills plateau

Sooner or later, you’ll get to the technical plateau. By that time, you’ll have so many successes under your belt and can detect potential pitfalls easily. Then, what next?

There are tons of ways to extend your impact and that is the way people become even better engineers. For example, I doubt if Anders Helsberg is still writing a lot of code, yet his ideas continue to empower and influence millions around the world.

Think about that, how do you scale your influence and make it possible to touch the lives of thousands of people? Are there engineering problems crippling your organization? Process pitfalls to improve with huge impact? Education ramp ups? There are always challenges to solve and problems to fix.

5. Choose career investments carefully

How would you set up an investment portfolio? Would you just go about investing in everything? Nope, you would evaluate the risks and benefits, consult experts and then invest in a select few areas while ignoring other areas.

You could spread out your risk by investing in a wide area but doing this excessively dilutes your returns. Conversely, investing in only company could be very risky too. Thus, it’s generally advised to spread  out your investment portfolio

Careers are investment portfolios. A typical career spans a long period ( upwards of 30 to 40 years) and shares some similarities with investments:

  • technologies, frameworks etc -> investment options
  • time -> funds

Just as you wouldn’t jump on every new fund, why would you do the same with your career? There is no harm in taking measured risks in careers but you should be strategic and know what your end goal is.

Every now and then a new framework pops up in the news. Before, I’d hop on the bandwagon and try figure it out. Nowadays? Well, if it really piques my interest, then I might spend some time learning about its core design principles and problem-solving approach.

If it neither solves any of my problems nor brings anything new to the table, then no thank you; I’d rather continue nurturing my current investment portfolio and hedging my bets.

Think about your bets and stick to them.

Conclusion

I am still learning and pray I continue. One thing that has struck me as being really critical is the will to try. We don’t know if something would work out or not however we can always try and then learn from the outcome (success or failure).

Don’t give up – continue learning and growing.

Influential Books for programmers


I try to read a lot of books. Over the years, my ‘taste’ for books has been refined and some of my criteria are listed below.

Book Impact Scale

  1. Length: The 200 – 300 page range is just about right for a technical book. Longer books contain a lot of fluff and repetition. Sometimes I get the feeling that the author had to meet some minimum page count.
  2. Thought-provoking: I love technical books that challenge the way I think, enable me to see things in alternative light and exercise my ‘thinking’ muscles. Isn’t that the goal for reading after all? To broaden horizons and learn alternative viewpoints.
  3. Inclusion of challenges: I like books that provide practice exercises. This cements the new ideas – it is easy to ‘think’ you already know something when you don’t. Solving a problem is the litmus test for understanding.
  4. Enduring: Every field has its ‘classics’ and they exist for a good reason too. If a 30-year old book is still in print, has lots of good reviews and teaches fundamentals; then chances are high it is a GOOD book. Add bonus points for language-agnostic themes.
  5. Simple: Most books fall short in knowledge delivery by employing obscure terms. This might be due to a misassumption of expected readers’ skill levels. Clear straightforward explanations make for easy reading.

Now, here come the books (listed in no particular order).

1. The Pragmatic Programmer

Excellent read. Medium sized at 352 pages but bursting at the seams with sagely advice. The authors used lots of interesting stories to drive home the points. Some of the lessons that have stuck with me include are:

  • Coincidental programming – you don’t why or how the code ‘works’. If adding that statement makes it ‘work’, then it might also cause bugs you can’t explain.
  • Learning:
    • Learn a new language every year
    • Master a single editor (got me to learn vim)
    • Become a command line expert
  • The broken windows theory – quickly leads to software rot
  • Programming close to the problem domain (SICP book did this too)
  • Being proud of your work – craftsmen are supposed to be proud of their work.
  • Bringing about the change you want
  • Test your software else your users will

Definitely influenced me in a big way and probably due for a reread now.

2. Structure and Interpretation of Computer Programmers (SICP)

A master classic – one for anyone who wants to be a master programmer and has the patience to complete all the exercises. Reading the book without solving the questions would make it difficult to fully absorb the goodness.

The authors painstakingly organized the book – the pages flow so smoothly that the increase in complexity is almost imperceptible. You get exposed to a huge slew of programming paradigms (logic, functional, OOP), build cool stuff (8 queens solver, calculus solve, an interpreter and a compiler) and also learn some CS stuff (streaming, Ackermann functions, Huffman etc).

The biggest change for me is getting to understand that programming is all about solving the right problems using the right abstractions and in a logical and well-ordered manner. Languages don’t matter that much – they are just tools for shaping processes and conveying our thoughts. Java, JavaScript, Python, C, C++ are all beautiful languages. If you don’t know how break down problems, evaluate alternatives and create solution pipelines, then it doesn’t matter what tool you use – the end result may work but wouldn’t be a masterpiece.

Give a master carpenter the minimum tools he needs and he can create something beautiful. Give me a fully stocked wood workshop and while I can craft a table it probably would not come close to what the master did with his limited tools. My table might work but I probably won’t get any appreciation for it. Think this same way about programming.

For example, here is how they explained list reversal:

To reverse a list, just append the first element to the end of the reversed sublist

Be warned though, this is probably a year-long journey but one that you must should undertake. It’s freely available and you can check out my repo of solutions too.

3. Code Complete 2

I had always wanted to read this book but I couldn’t bring myself to do that because it was over 1000 pages long. Finally, I gratefully found a strategy that worked – committing to a 30 min read window daily; mostly on the bus commute home.

The points from the book:

  • Avoid complexity at all costs; software is already difficult so make it as dumb as possible. Again, make it plain simple.
  • Always care about quality regardless of your state in software development
  • Create programming models in your language i.e. try to create domain abstractions in the language; this makes it easier to maintain and express ideas
  • Programs are meant to be read by humans first and then for computers next
  • Being open to other ideas and influences – no need getting stuck to some particular tools
  • Testing styles to ensure code coverage

It is a great book – a bit verbose at times but one that you should read too.

 4. Programming Pearls and more Programming Pearls

A bit old but still readable – it made it obvious how programming had advanced and developers could avoid knowing intricate computer details.

The two books are small and contain a lot of ‘pearls’ that make you think deeply; most revolve around events that happened during the authors’ time at the Bell labs. The coding examples might be outdated but the solid principles in the book aren’t.

For example, a book teaching the basics of motion from the 60s would probably still be applicable today; motion fundamentals haven’t changed much. Same way the author’s approach of problem definition, isolation and iterative improvements can’t be faulted.

Highlights

  • Back of the envelope calculations – made me realize how these axioms could influence the way you program. Now you know why these questions are popular at interviews.
  • Over-engineering a solution might help it cope with unforeseen load
  • Iterative improvement – first solve the problem, then improve the algorithm if needed.
  • Confirm algorithmic correction – it’s very easy to assume your algorithms work when they infact contain bugs.
  • Programming for resource-constrained environments – how do you sort 4 million nodes on a limited memory computer?

5. The Little Schemer + The Reasoned Schemer

The style was nothing like any of the others and I surprisingly found it enjoyable. It flows as a series of questions and answers although readers are encouraged to try before taking a peek at the answers.

The first few chapters were easy to breeze through (coming from a SICP background probably helped too) however things get more interesting from about chapter 9 or thereabout.

  • Provides a ‘formula’ for nailing recursion: fix the base case and then handle the recursive case

Learning recursion is easy, master the first part and then you can learn the remaining parts (pun intended).

  • Good explanation of the Y-combinator – slow build up but still challenging explanation of a hard-to-grasp concept.
  • Covers CS concepts like generators, memoizations, closures, Church encoding, currying, continuation passing and more.

Worthy of mention

1. The Algorithm Design Manual (TADM)

Normally algorithms books are all full of mathematical proofs (really important to understand). However, I like the TADM because it focuses more on applying those algorithms in practical context. The various stories by the author and his humorous quips make it all the more fun too.

2. Eloquent JavaScript

The book that exposed me to functional programming and started the long JS adventure. I always recommend it since it is an excellent beginner book, free (despite its high quality) and is just awesome.

Conclusion

Looking back, it is interesting to see the subliminal influence these books have had on me. I only realized some of these while writing this review.

What are your favorite books and how have they influenced the way you write programs? Share in the comments.

Advice for aspiring programmers


I have made a couple of mistakes over the years and wanted to share those pitfalls so upcoming programmers know what to avoid and what works.

1. Focus

You can’t do everything – I don’t think it is possible for one person to simultaneously be a pro at writing high quality code, network engineering, computer hardware and machine learning.

Some years ago, a much younger and naive me wanted to do everything in software engineering, use several operating systems and know enough about network engineering. Did I succeed at that? No, I just diluted my efforts and wasted loads of time (was fun though…).

Laser-like focus is essential as the field of computing is extremely wide. Decide the field you are interested in, narrow down a learning path and get your hands dirty. For example, if you choose software engineering, you’ll have to select one of three tiers: web/mobile/desktop. Once decided, just work on completing a sample project. The more projects you have under your belt, the better you become and the more learning you do.

2. Dedication

You probably won’t become good enough in one or two years. That learn * in 24 hours book? Gives you a feel for what the language syntax is but there is more than that to mastery. You need to learn language idioms, problem analysis and clean architectural design.

This take time – a lot of time: Norvig put it at 10 years; just like mastery in any other field. It is a long haul so set your mind at it and you should hopefully start seeing results in about 5 years.

3. Consistency

Little drops of water consistently dripping on the same spot can wear down rocks.

The rush of excitement you feel at the beginning is eventually going to wane. Once it becomes a dreary old routine, what will keep you going at it? Consistency and habits can compound in surprising ways.

Assuming you are 1% better each day; after a year; you’ll be about 37 times better. Conversely, if you lose 1% everyday, then after a year you’ll be 0.02 of what you were. This is an extreme example but …

Small incremental progress over a long period of time leads to big increases. For example, a developer that dedicates 25 minutes daily to improving his skills would have spent ~183 hours honing his skills after a year. That’s nearly a month of intense study time. Can you study intensely for one month without burning out?

Can’t afford the 25 mins a day? How many minutes do you spend daily on Facebook? Multiply that by 365 to see where your time is going… Oh.. add in some more hours for games, TV and Twitter too. You see little things add up quickly over long periods of time.

Now once you are consistent, what’s the next step?

4. Seek mastery before moving on

Now that you have focus, the next obvious step is to stick to a path. Frameworks pop up every second in software development but hopping around from one framework to another is ill-advised: you would likely end up as a jack of all frameworks and master of none. I agree and encourage exploratory learning but jumping on every fad bandwagon is going to get you nowhere. Rather, make a strategic choice, stick to it and commit to learning it deeply.

Does this make you less marketable? Afterall, you want to be that guy who knows how to write code in 10,000 languages. Mastering a few increases your worth – you can now charge people more for expertise in your domain. Think of it this way, if you want a dining table masterpiece, what kind of carpenter would you prefer? One that specializes in dining tables or a generalist?

5. Learn the fundamentals

I can’t emphasize this enough. Let’s take a story: EmberJS and AngularJS brought about widespread adoption of 2-way binding across UI interfaces and this in turn triggered the proposal for object.observe as a JavaScript language method. More recently, the new path has been reactive programming and virtual DOM, eliminating the need for observables. You guessed it, object.observe got retired from the language. Now, a counter example, Dijkstra’s algorithm has been around since 1956 and it is still in use after 60 years! Can you beat that?

You have to know algorithms, data structures, operating system fundamentals, caching, Mathematics, etc. These concepts are language agnostic and provide a solid bases for solving new problems. That cool functional reactive somersaulting framework you are harping about today? It may be gone in 10 years. Fundamental CS knowledge? Evergreen, safe investment.

6. Be Humble

There are two things you should keep in mind:

  • There would always be a programmer better than you
  • You probably overestimate your skills

Do you disagree with me? Check out your ranking on the programmer skills matrix. Next, read Micheal Church’s excellent adders analogy. Unless you are already at 2.5+, there is no need to brag about your skills. Instead, quitely keep pushing the limits and some day you hopefully would earn bragging rights.

Humility is a great characteristic to have – there are a ton of things you do not know and need to know. And it takes time to reach the levels others have reached. Even if you think you are a rockstar, people generally appreciate a humble confident programmer who doesn’t rub his ‘magnificence’ into their faces all the time.

Need a yardstick to measure your progress? Periodically check back on both lists to track your progress.

7. Learn how to communicate

Most of software engineering is not about writing code – a large amount of time is spent discussing projects, evaluating designs and interacting with customers. The ability to clearly convey thoughts is one most programmers lack and it is a clear differentiator.

Being able to express yourself means you can get help with problems because you can explain them to others, it also means you can convince people to join your open source project or even adopt your architectural design. All these come from being able to sell yourself and your thoughts.

Communication is not limited to speech only though, writing well is another part. Want to improve? Write more, speak more, volunteer to be a spokesperson – practice makes perfect.

8. Get a mentor & be visible

Stand on the shoulders of giants to see farther. You need to avoid making pitiable mistakes if you can, accelerate your growth and ensure you are on the right path. Great mentors help you grow, introduce you to their networks and provide insightful feedback. They have a lot of experience which you don’t.

Don’t hide under a rock! People should know you for what you do!! Would you buy Microsoft Word if you never heard of it? Seek out peers in the community, interact with them and share ideas. Help one another grow and help influence the community. Being visible makes it easier to find mentors, jobs or even new freelance opportunities. Don’t want to go out? Then create a blog, comment on blogs, release software on Github or participate in fora.

9. Be open

No, your favorite language/IDE/tool is not the best thing since bread and butter. It has its own flaws and strengths.

You can have pretty strong opinions on anything, for example, favorite languages. However what you shouldn’t do is get so tied to some language that you refuse to try out any other language. There would always be situations where your favorite tool doesn’t cut it; in such scenarios, find the best tool and deploy it.

A Toyota Camry can probably carry logs of wood but you are better off moving such with a heavy freight truck.

I used to think I understood recursion until I read SICP; I was forced to write programs without resorting to variables and suddenly, simple toy programs became quite challenging. Exposure to various tools will empower you with various problem solving approaches. This is always a good thing since software engineering is all about solving the right problems.

10. Startups can be overglorified

I think most times, the start-up concept is overglorified – it involves a lot of work, huge risk and great rewards on success. However, the idea that once you make that awesome piece of software, it’ll automatically sell itself is mostly false. That rarely happens.

The saddening part however is that most of us do not know how to deliver good software, talk less of selling software. Yet, they want to run the business. Imagine you handing over a truck on the highway to an unqualified driver who barely knows the ropes. What’s that? A recipe for a crash right? Yup, then why do we do the same with startups?

Software is typically not the critical part of most businesses, rather meeting the user’s need is. You need to learn how to write software first, then learn about business and entrepreneurship – creating a MVP, build-measure-learn cycles, customer interactions, acquisition funnels, maintenance and support, delivery pipelines, execution and meeting competitors. Now, you know this, you can see the bigger picture – the software is not the goal. The business is the goal – the best piece of software that no one uses is a failure.

There is nothing bad with working in an established shop for some time to learn the ropes, creating connections and learning how business is run. You can even start testing the waters and at least you have a safe cushion if things go south.

And that’s it! Did I miss out on anything? Do share your thoughts in the comments or follow me on Twitter.

10 years of programming: Lessons Learnt


I wrote my first program around 2006 or thereabout – it was some toy program for the FORTRAN course at school. Ever since then, I have been given the opportunity to take on more challenging programming tasks. It’s been a heck of a journey for me and I am very grateful for the learning, the mentoring from rockstars and the chance to do exciting work.

Looking back, I have learnt a couple of lessons the hard way and wanted to share some of these so that other engineers know what to avoid. These are my rules of thumb currently (in no particular order). Enjoy and let me know your thoughts in the comments!

1. Write plain simple code

Code is meant to be read by computers. Computers and compilers could care less. So if you are feeling super smart then write in 0s and 1s; otherwise, please write extremely simple code.

When exceptions arise necessitating unorthodox or clever approaches; please do add comments and also create documentation. This would help the poor fellow who’s going to maintain it in the future (most likely you).

But why should we avoid clever code? After all, how else does a programmer show off his cool tricks?

if all you have is a hammer, everything looks like a nail – Abraham Maslow, 1966.

The problems associated with smart code are plenty –

  1. They require a higher level of concentration to understand
  2. They are prone to bugs since the smart fluff can hide bugs
  3. They can nearly always be simplified leading to smaller code and bug footprints

Suppose you have a pounding headache and your doctor orders you to buy any non-steroidal anti-inflammatory drug (NSAID). Imagine trying to decipher what NSAID stands for while coping with the raging headache. Not fun eh… What if the doctor told you to buy Panadol (substitute with Tylenol or Ibuprofen etc.)? That’s the similitude of simple vs smart code.

One way to simplify code is by avoiding surprises and establishing consistent patterns. For example, consistent naming patterns, predictable branching styles or standardized design patterns.

Why does this matter? Well it allows someone else to pick up your code, see the structural pattern and then extend it. There is a common language that everyone understands which makes deviations and buggy approaches easy to spot.

But writing dumb plain code is not so easy; so let’s go on.

2. Make it work then clean it up

While working on Odo, the JavaScript Stream library I wrote last year, I  agonized a lot over getting the perfect style. At times too, I catch myself trying to create a masterpiece at the first attempt.  The truth however is that this doesn’t happen often; maybe if you are Donald Knuth.

Most authors do free-flow writing while trying to get their thoughts together; afterwards, the editing and clean up occurs. Why do you think we have book editors? Coding is similar too; rather than rewrite that 3-line function 20 times, why not get the app working and then start refactoring? It’s much faster and code reviews would help catch coding issues you might have missed (just like editors with books).

Typically I just write and get a feature out, then I take a fine sieve to it. All the rules now come into play: DRY, YAGNI, code simplication etc. I do try my best to make it read like poetry (yes I am serious).

3. Never make the same mistake twice

Yes, once is good enough. Twice is bad.

You shouldn’t be shy of making mistakes, no human is infallible but what should scare you is making the same mistake more than once. Do a retrospective after completing each feature to learn what could have been done better and learn from missteps.

I recently took a long amount of time to implement some core software in an unfamiliar area, why? My initial assumptions about existing data structures were wrong and I found out a while later. It wasn’t so difficult to pivot to the right structures and then I ran into another issue – the helper functions I relied on didn’t behave as I expected they would.

Looking back, I could have written tests earlier (instead of later) to validate expected behaviours and also clarified assumptions with senior engineers. Lesson learnt.

A respected senior engineer explained it thus to me:

Junior developers might take a day to realize they are on a wrong path while senior engineers have enough experience to avoid such drains.

Good judgment comes from experience which involves making mistakes and learning. It’s OK to stumble and make mistakes, but please review each one and learn from that.

4. Know where you want to go

How do you get to China? Start heading east. Once you have a target, it is easier to calibrate your efforts and see if you are moving in the right direction. Unfortunately, most software developers never set goals and then get caught up.

The software engineering profession is remarkable for having rapidly changing and moving targets. Currently, the big areas include cloud computing, security, big data, artificial intelligence and AR/VR. Are your skills stagnating or positioning you to shine?

Having a general blanket target (e.g. I want to be a 10X programmer) doesn’t work; there has to be some specificity to it. So for the one who aspires to be a 10X programmer, the next question would be in what areas do you want to excel? Are there examples of such 10X programmers to copy?

Think again; do you want to extend the frontiers of computer science? Design the next Hadoop? Or author a widely popular book / open source software piece.

Know where you want to go and then start walking.

5. Task Breakdown and Estimation

You are to build a brand new calculator app; your manager wants it delivered as fast as possible. How do you estimate the amount of work required and when it’ll be ready for the customer?

How do you eat an elephant? One bite at a time…

The calculator project can be decomposed into modules like the core engine, the user interface and the data storage mechanism for calculation history. Each component should be broken down into smaller chunks. After breakdown, the engineer typically knows the pieces of work that needs to be done before he completes the project and can start implementing each phase.

This quickly becomes tricky when there are multiple people working on the same project and you need to coordinate integration efforts, release dates and project quality.

Always keep in mind the end goal of shipping the software and prioritize tasks accordingly. You want to maximize your return on investment so pick tasks that move you closer to the end goal.

6. Quality

Nope nope nope, testers shouldn’t find simple bugs in your code. You only want them to find bugs that occur when you press 10 buttons simultaneously while doing one-handed push ups standing upside down.

If you don’t find and fix your bugs; your customers will. And they won’t be happy.

Do the right thing; test the happy path first. Then, verify that there are no monsters lurking in the dark corners. Take some time, maybe 30 minutes, to rigourously exercise the code.

I typically write unit tests while completing the feature. Then once it’s ready, every discovered bug gets its own unit test. This helps to protect against regressions in the future. Aside, those unit tests provide active ‘documentation’ of the behaviour of the software.

Don’t compromise on quality – it’s the sign of good software and a great programmer.

tl;dr?

Have been writing programs for some time, made mistakes and learnt lots of lessons.

Do share your thoughts in the comments – would love to hear them!

The Effective Programmer – 3 tips to maximize impact


Effectiveness, (noun) : the degree to which something is successful in producing a desired result; success.

Over the years, I have tried experiments, read books and watched several talks in a bid to improve my effectiveness. After a series of burnout and recovery cycles, I finally have a 3-pronged approach that seems to serve me well.

1. Learn to estimate well

2. Use the big picture to seek opportunities

3. Continuous Improvement

Lets discuss these three.

1. Estimation – the bane of software development

Reliable coding estimates accurately forecast when feature work will be done. But when is a feature done? Is it when it is code complete? Test complete? Or deployed? Most developers wrongly associate code complete with test completion or deployment ready. This explains arbitrary estimates like: “Oh… I’ll be done in 2 hours”; such estimates typically miss the mark by wide margins due to error compounding. Let’s take a simple bug fix scenario at a fictitious software engineering company.

  • Bug is assigned to developer John SuperSmartz
  • John SuperSmartz reads the bug description, sets up his environment and reproduces it
  • He identifies the cause but does some light investigation to find any related bugs (he’s a good engineer)
  • John designs, implements and verifies the fix
  • Gets code review feedback and then checks in

Any of the intermediate steps can take longer than estimated (e.g. code reviews might expose design flaws, check-ins might be blocked by a bad merge, newer bugs might be discovered in step 3. etc). Without such an explicit breakdown, it becomes difficult to properly give estimates. Don’t you now think the 2-hour estimate is too optimistic?

Personally, I use kanbanFlow (I love their Kanban + pomodoro integration) to decompose work into small achievable 25-minute chunks. For example, I might break down some feature work into 8 pomodoros as follows:

  • Requirements clarification – 1 pomodoro
  • Software design and test scenario planning – 2 pomodoros
  • Coding (+ unit tests) – 3 pomodoros
  • Testing and code reviews – 1 pomodoro
  • Check-in + estimation review – 1 pomodoro

Some of the things I have learnt from using this approach:

  • I grossly underestimate feature work – the good side though is that this planning enables me to improve over time
  • I know when to start looking for help – as soon as a task exceeds its planned estimate, I start considering alternative approaches or seeking the help of a senior technical lead
  • Finally, it enables me to make more accurate forecasts – e.g. I can fix x bugs per week…

2. See the big picture

A man running around in circles covers a lot of distance but has little displacement. In optimal scenarios, distance covered equals displacement while in the worst scenario, it is possible to cover an infinite distance and have a displacement of zero.

Imagine working for several days on a feature and then discovering major design flaws that necessitates a system rewrite; a lot of distance has been covered but there has been little displacement. Working on non-essential low-impact tasks that no one cares about is neither efficient nor effective. Sure they might scratch an itch but always remember that the opportunity cost is quite high; the lost time could have been invested in higher priority tasks with a larger ROI.

Whales periodically surface for air and then get back into the water to do their business; so should engineers periodically verify that priorities align with company’s goals. It’s possible to get carried away by the deluge of never-ending feature requests and bugs fixes; an occasional step back is needed to grasp the whole picture. Here are sample questions to ask:

  • Where are the team’s goals?
  • Does your current work align with company goals?
  • Skills acquisition and obsolescence check
  • Opportunities for improvement?

Personally I try to create 3 to 4 high-impact deliverables at the beginning of each week and then focus on achieving these. Of course, such forecasts rely heavily on productivity estimates.

3. Continuous Improvement

Athletes consistently hold practice sessions even if they don’t want to because it’s essential to staying on top of their game. The same applies to pretty much any human endeavor – a dip in momentum typically leads to some loss in competitive edge. The software engineering field, with its rapidly evolving landscape, is even more demanding – developers have to continuously and relentlessly learn to stay relevant.

Staying relevant requires monitoring industry trends vis-à-vis blogs, conferences and newsletters. There are a ton of resources out there and it’s impossible to follow every single resource, rather it is essential to separate the wheat from the chaff and follow a select high-quality few.

Learning and experimentation with new technologies naturally follows from keeping abreast of developments. A developer might decide to learn more about the software stack his company uses, logic programming or even computer science theory. Even if his interests are totally unrelated to his day-to-day job, independent learning would expose him to new (possibly better) ways of solving problems, broaden his capabilities and might even open up new opportunities. I prefer learning established CS concepts to diving into every new db-data-to-user-moving framework.

Opportunities abound such as learning IDE shortcut keys, terminal commands, automating mundane tasks and so on. Ideally you want to start simple by selecting the area with the highest impact-to-effort ratio and then dedicating a few minutes to it daily. Over time, the benefits start to pay off.

And that’s about it! Do you have other suggestions?

Like this post? Follow me on Twitter here or read some of my top posts.

1. Code is poetry: 5 steps to bulletproof code

2. So you want to become a better programmer

World Class Nigerian Software Engineering: Are we there yet?


Jason of Iroko recently announced mouth-watering offers for developers and this triggered a long discussion about software engineer pay in Nigeria. The discussions on techCabal’s radar got me thinking about software development in Nigeria, do we have enough world-class talent or could we be better?

The software engineering field has spawned a wide variety of roles: product managers specialize in product design and customer interaction, software engineers write code and ensure quality while devOps folks handle deployments and infrastructural issues. Interactions between these fields lead to the delivery of great software. More importantly though, these distinct fields are essential for specialization, which is critical for growth.

At small software firms and startups all over the world; engineers are expected to be responsible for the whole gamut of software development – UI design, development, testing, deployment and customer support. Not to say this is bad; sometimes there is a genuine need for generalists and developers can acquire great skills by working in such places. However, as the popular saying goes, jack of all trades, master of none. I doubt if it is possible to be well-versed in all the numerous fields of software engineering; moreover standard practices like pair programming, build-measure-learn and one-click deploy weren’t created by generalists.

Building software involves a lot more than cranking out code and shipping software artifacts; there is a need for thought leaders advocating best practices and innovating new approaches. And this is why specialization is essential. Now, looking at the Nigerian software scene, can we have world-class experts without specializing and going deep? In recent years, the number of start-ups, incubators and software shops in Nigeria has ballooned and demand for engineers has gone up. However despite the large number of excellent Nigerian software engineers, we still have a disproportionately small number of thought leaders.

Some might argue that there is a dearth of thought leaders and specialization because the Nigerian software industry is young. I disagree, rather I think we have lots of engineers with significant experience shipping high quality software and meeting deadlines. Writing software for the Nigerian market is fraught with challenges and surely there must be some best practices to share, unfortunately, we do not hear about their stories. For example, why isn’t Konga publishing on their tech blog? How about the Terragon tech gurus writing white papers about Adrenaline? Nairaland? Such efforts drive technology adoption, improve the entire field and might even bring in new talent!

Let’s talk about change…

Sadly most computer science graduates do not know how to write code; in contrast, fresh graduates in other places complete non-trivial projects before graduation. This gap puts a significant drain on firms who have to invest heavily in training and mentoring fresh employees until they are proficient.

The educational sector has to be fixed; a catch-them-young scheme aimed at motivating undergraduates and secondary school students should help ensure a good supply of trained developers. The great work being done by Andela and CTI is worthy of mention: they are creating realistic environments and this is a step in the right direction.

Top companies, incubators and the existing thought leaders can accelerate growth by creating conferences, meet-ups and talks. These will provide opportunities to share ideas, drive networking between potential employers/employees and increase collaboration. Furthermore, these create opportunities for upcoming engineers.

Developers need to up their game too – it’s just not enough being the best programmer out there, we need to contribute to community too. Do something – create a tech blog, give a talk at a meet-up or mentor upcoming developers. It also involves being open with ideas, learning on every project and driving good practices at and outside work. I’d expect programmers to be more ambitious and aim to change the world (enough of apps that move data between databases and devices). Seek challenges, learn a lot (e.g. computer science, entrepreneurship, product design, methodologies, testing etc) and then inspire people.

Our ‘unique’ environment involves piracy, intellectual property disregard and tough economic conditions; notwithstanding I believe we have the potential and can do it. Rome wasn’t built in a day and creating a world-class industry will take time. However, if we try, we might get there faster. Hopefully someday soon we’ll have respected Nigerian thought-leaders actively pushing the boundaries of software development. Our very own Brenden Eichs, Joel Spolskys and Bob Martins…

Let’s all help create a thriving Nigerian software industry – one with international acclaim. And then we can aim to get the 6-digit dollar salaries too…

This post first appeared on TechCabal.

Related

1. Opennigeria… the time is now!

Code is Poetry : 5 steps to bulletproof code


I recently read a Quora post about a meticulous cleaner who would not let the smallest blemish evade him – he’ll stop, fix the flaw and then step back to admire his work. The narrator admitted that the cleaner’s platform was one of the neatest he had ever seen around the world.

Morale of the story? The same principles apply to software development – programmers have to love their craft and put their best into making it stand out. Going the extra mile in simplifying complex code, removing dead code or writing unit tests help to increase the life of a code base. These actions might appear to impede development speed but again, what is the definition of slow? As a former colleague used to say: “we sometimes have to slow down to go faster”.

Poorly written software always comes back to haunt developers in unimaginable ways; such code will effortlessly conjure monster bugs of all types (e.g. heisenbugs, mutatorbugs, linkerbugs and more). Removing bugs in bad software is also a nightmare as fixes might spawn even more abominable bug monstrosities (without unit tests, how do you verify? ). Which would you prefer? Disappointed customers, having to work weekends and eventually rewriting the same code versus doing it well upfront. The latter does sound faster to me…

Enough ranting, how about a couple of suggestions on how to write bulletproof code?

1. Know what you’re doing

Have you ever had to work on impossibly cryptic code? To all programmers who disparage others who do not get ‘smart’ code, here is a beautiful quote from E.F.Schumacher:

“Any intelligent fool can make things bigger and more complex… It takes a touch of genius – and a lot of courage to move in the opposite direction.”

Most ‘smart’ code pieces are indicative of shallow problem domain knowledge. Most times, these pieces can be simplified by taking away pieces until the problem the author was trying to solve becomes clear.

The  first thing you want to do when approaching a problem is to stop and think deeply. Stop! Don’t even start that IDE!! Do you understand the problem thoroughly? Do you know what issues might arise? How about testing and completion criteria? Once brainstorming is done, then find the simplest, fastest and most effective way to clearly convey your solution. Always remember that programming languages are vehicles for expressing your thoughts.

Without fully understanding the task, it is possible to end up with over-complicated code or even worse: ‘smart’ code. Please do not create breeding grounds for evil bugs – developers have better things to do with productive hours. Simplicity is the true smartness…

2. State modification is the root of all most evil

Functional programming purists tout FP as being less susceptible to bugs because it avoids modifying state. The best functions are like Mathematical functions, e.g y = x². Why are they good? Well they always give the same results for the same input and have no extra hidden internal state lying around.

I like using functions a lot; composing them into larger pieces and building up systems based on them. Such systems rely heavily on piping – function outputs are passed to other functions as inputs. Clearly designed functions are great as they communicate intent (e.g. square vs x²), make it easy to follow the author’s thought process and provide simplifying abstractions.

Watch out for the double-edged sword of modifying state in functions, it is the root of most evil bugs. Having the wrong assumptions about state changes, obscure manipulations (i.e. inadvertently modifying loop invariants in functions) and improper naming inevitably lead to trouble.

Whenever possible, use a functional programming style that relies on stateless functions. Hopefully this saves you a lot of debugging pain – you can thank me later…

3. Clean up as you go

What is your typical reaction when you come across horrible code? Do you sidestep the mess (I didn’t write it…) or do you attempt to fix it?

Just as people are more likely to drop trash on a dirty sidewalk compared to a clean one, developers are also more likely to write horrible code if the original source code is bad. This attitude is one to avoid at all costs; if you can’t invest in cleaning up the dirt, at least don’t add to it! Remember the boy scouts’ rule – always leave the park better than you found it.

Ideally you should clean up the code as you work in it – it shows that you care and helps prolong the life of the code base. If you need to strike a balance between adding new features and fixing technical debt (which might not bring new business value), then carve out a small block of time (e.g. 30 minutes).

If you want to know why this is important, then you should read about the broken windows theory (not yet, finish the post).

4. Love your craft

The best craftsmen are immensely proud of their work and achievements; they try to show it off to everyone and won’t stop talking about it (even if we do not want to hear about it ).

You should be proud of your code. Seek feedback; what looks beautiful to you might be terrible code – perspective, as they say, is subjective. Don’t get defensive and hurt over review feedback too – it’s the code dude! Not you!! So swallow your pride, learn and improve the code. Growth involves some pain.

Spend the time to think about elegance, simplicity and brilliance; the extra time and effort pays off. Which would you prefer of the two outcomes?

  • Creating a masterpiece in 6 hours
  • Creating a ‘hacker-piece’ in 2 hours and then spending 10 hours in maintenance (bug fixes, extensions and changes)

It’s a trade-off; I don’t like hunting down bugs – I do it if I have to but I’d rather not…

5. Break your software yourself

There was a dramatic moment during the début of the Microsoft Surface – the presenter dropped it (intentionally) and everyone gasped! Why would anyone want to drop a tablet? There are countless woeful tales of smashed screens, internal damage and memory loss. However, the presenter knew the product’s limits and knew he could do that. Can you break your own software? Do you know its limits?

Go ahead! Break it!! How else would you know its limits? Nearly every piece of code can be broken – a function that calculates 1 + 1 will give a wrong result on a computer with only 1-bit of memory (yeah, I wonder how you’ll even get such a computer). 

It’s a shame if customers find obvious bugs that should never have gotten into production. Customers who run into show-stopper bugs are typically not happy and unhappy customers can consider alternatives. Broken software can cause huge losses (goodwill, money, credibility, tarnished brand images etc) so you do not want to take this lightly – unless you fancy going out of business.

In these days of web applications, your customers will do a lot of testing for you – the more users you have the more weird scenarios people would put your software through. Do not worry about the extreme cases, the normal expected way has to work well though.

So try to break your code yourself before you call it done. This makes it more robust and you get to know of the corner cases. Even if you do not fix them, such knowledge enables you to handle customer complaints.

Inshaaha Allaah I hope to write about how to break software some time soon.

Now go and write some better code…

Related