The Unethical Developer's Guide to Personal Success

Here's an article that I wanted to post at the start of the year, yet got stuck debating whether I should even do that at all, whether it would send the wrong message. It will probably be fine. Frankly, 2022 was an okay year for me and I made the development on some projects a easier for my teammates, as opposed to things I've seen in other codebases over the years, including freelancing.

The premise

We like to talk a lot about how to become better developers, how "10x developers" are actually people who help others be more efficient and encourage them to be their best selves, and thus make the whole team benefit. And yet, I've seen projects where the developer experience (DX) is so bad, that it makes you ponder whether the people behind the project were purposefully malicious, or just incompetent.

Now, Hanlon's razor would urge us to adopt the more positive outlook on things:

Never attribute to malice that which is adequately explained by incompetence.

(the original definition is arguably a bit more rude, paraphrasing here)

Admittedly, software development is complex and things can get out of hand, but in those circumstances, most people are likely to become not "10x developers" but rather "0.10x developers", so I think we should talk about some of those bad circumstances a bit more. People online sometimes joke about how writing uncommented or hard to understand code is "job security", but what if there are people out there who believe that unironically?

More so, what if someone was to actually write a guide for that sort of mindset in particular:

unethical developer title

What would such a guide look like, that would prioritize one's own success over everything else?

But before that...

A slightly late holiday reminder that charity is cool

Before we get into the guide, I'll also briefly mention that I contributed some money to a few local charity projects in my country for the holidays that I'm proud of:

charity 1

I also donated some money to One Tree Planted, in addition to my regular contributions to Wren, a platform that lets you pay to offset your CO2 footprint:

charity 2

Perhaps it would make more sense to mention this around the holidays to urge others to do the same, but it definitely felt like one of the better things you're allowed to do, if you're in a financial position to do so. Of course, none of the charities are perfect, nor are my efforts to be a good and ethical person, yet at least I try when possible: be it being friendly towards others, professional in the workplace or even charitable.

However, not everyone is like that. So, without further ado, let's get into it.

The guide

Note: this is satire. If anything, it should serve as a guide of what NOT to do, though hopefully make you think about how to actually do things better, if any of this sounds familiar.

Many would have you believe that your goal as a developer is to work together with others and help them as much as possible, so that everyone succeeds. It has also been said that the mythical "10X developer" is not someone who writes code with 10 keyboards, but rather is someone who helps 9 other people achieve their goals as well. Today, I am here to tell you that instead it's a zero sum game - from your point of view as a developer, there might instead very clearly be "winners" and "losers" in this game. There are only so many promotions and raises to go around, there are only so many people that will be viewed as those who get things done within any organization. This might even directly coincide with who gets chosen for layoffs as well.

What matters. The truth is that you shouldn't care too much about the success of your co-workers, your project, or even your company as a whole.

Back in the day, the Agile Manifesto suggested that we should prioritize working software, which implies that you should know what to build, be able to do so and execute on that goal. But the launch being a success or your boss making more money won't always lead to recognition or some other benefits for you personally, if you're just a cog in the machine. Instead, you should focus on your own success and furthering your personal achievements, or even just what others think are your achievements. Do not put in any thankless or low-visibility work with the intention of improving how things are done and look for a better paying job in the background.

While in the company, however, the downfall of others will be your victory as well. You won't be compared against all of the developers in the job market. Most likely, you'll be compared against others who work in your company, as well as those who work in your project. So, if their output (perceived or real) is worse than yours, it will be pretty clear that you're "better" than them. And that means your opinions being listened to more, as well as you being irreplaceable and probably getting more raises. You should find out whatever metrics you are judged by (such as lines of code written) and optimize for those.

You also don't want to be ousted eventually, so you need to look for environments where people don't know any better. You should work in an environment where they don't have a strong engineering culture, something that you surely won't try to change. Work in a company that struggles to ship software without delays, with the occasional outages, where you swoop in and help them along, but without anything that could help the company avert them in the future - neither blameless post-mortems, nor root cause analysis, which would otherwise be of great benefit!

A canary for finding a company like this would be them doing "agile" but not having sprint retrospectives (or anything like that), where they genuinely try to explore how to improve their work. Instead, go for one of those Jira driven development companies where a lot of focus instead is on resolving issues and jumping from one sprint right into the next one, anything else being considered a "waste of time", same as with hackathons, internal seminars or pilot projects that could lead to improvements.

Your mindset. Don't just join a company and ask: "What problems can I solve? How can I help others work even better?"

Instead, ask a few simpler questions:

  • "Who do I need to impress? Is it the bosses? The HR people who don't have an engineering acumen but will decide on raises?"
  • "How can I make myself seem more impressive than others? Do I need to increase my own output, or have that of others get worse?"
  • "How do I align the company's goals and those of my role in the company with my own personal goals, and not vice versa?"

In the modern day world, it doesn't matter whether you're the best developer in the room, it matters more whether the people in control of raises think that you are. You're going to work on your people skills, which anyone might benefit from, but for all of the "right" reasons - your own benefit. Have you heard the saying:

If you're the smartest person in the room, you're in the wrong room.

Well, it's going to be the opposite in your case. Establish yourself as an authority, as someone who affects how things are done within it, without ever taking responsibility for what happens in it - after all, not managers are well versed in the technical aspects, as well as what causes might have what consequences. A lot of power and relatively little responsibility is the correct position for you to be in.

The zero sum game aspect means that you should simply make your work as easy for yourself (and your achievements more loud and noticeable), than those of anyone around you. For example, you can write a bunch of useful scripts or even tools to help you accomplish your goals more quickly in your project, but you're under no obligation to share them with anyone else, since then you'd actually need to support them. The same applies to IDE run configurations and such, so your projects would run easily for you, but would be a black box to any newcomer. Having a Docker Compose file that launches a local environment goes a long way to help anyone, so don't.

So, onward to a few other practical tips.

Don't write code comments or contribute to knowledge-bases. Claim that they're useless and code should be "self-documenting", ignore the need to explain the business context behind certain requirements, don't look for ways to do so. Claim that Wiki pages will get outdated quickly and thus will be worse than not having them.

Look at this video by Dylan Beattie, I'm sure that you can tell which of those two scenarios your workplace should be more like. Don't have onboarding guides or knowledge-bases, don't focus on knowledge transfer, after all, you knowing how the project works is all that matters. Anyone joining your team anew should be confused about both the system, as well as the business jargon surrounding it. Also discourage thorough tests that could otherwise demonstrate some of those requirements and be living documentation.

If you meet opposition, just claim that a lot of this effort is needless and only will prevent your team from shipping new features in a timely manner. Better yet, nitpick cases where any existing outdated docs are lying to you, that will almost always be the case. See if you can put any existing documentation in binary formats, like those used by Microsoft Word and throw them in a file share somewhere, instead of using a Wiki that indexes everything, or even plain text formats like Markdown. Being able to search through your documentation is great, as is having your documentation as a bunch of Markdown files, because working with text files is really easy, so of course don't do that!

Frankly, most teams out there could benefit even from something as simple as a self-hosted instance of BookStack. It's easy to run and is useful for organizing all sorts of information, though you might as well keep all of your useful notes private and be the only one who benefits from them.

Slow down others, by making them wait. Embracing asynchronous communication can be good when people are busy and have different schedules, so prefer synchronous communication when answering questions instead.

Nobody should be able to proceed with their work, unless they can ask questions about the code to the person who wrote it before them. Be okay with your application not running directly after getting the source code, with no helpful error messages to guide people to solve their own issues. Not having onboarding guides for adding new developers to the team will make them ask for help in person, or in a call, which will make the person in question drop whatever they're doing to attend it. This will make it seem like they're slow on the uptake and slow them down due to constant context switching.

I made a quick guide on how to communicate better asynchronously. Do the opposite, both in person, in calls and in writing: either don't respond with enough details, or drip feed them information that's not everything they need to move on. When they ask you how to do something, lean towards the XY problem and question whether they're even trying to do the right thing. Better yet, don't respond to their messages or requests for calls when you don't feel like it. Leave a message that you're busy, block out your calendar until you feel like getting back to them. Anyone who actually takes the calls will put themselves at a disadvantage because of context switching.

If anyone says anything, just respond that you're busy or that it's "easier" to just tell them and show them when you'll be done. This is especially true if you need to communicate remotely and use a platform that is notorious for unusable audio/video for when you share your screen. Oh, and collaborative code editing plugins? Forget about those, embrace wasting time so you can later claim that those other developers need a lot of help from you.

Nitpick everything, especially in code review, except for your own code. Bomb people in code review with "suggestions" about things that absolutely do not matter, but will make them spend more time refactoring to fit your whims.

Want them to name variables differently? Everyone is guilty of this, so do as much bike-shedding as you can. Have you written an obscure abstraction that is kind of similar to what the person is trying to do? Why not send them on a wild goose chase that will lead nowhere after they'll waste hours trying to get the old code working for their use case, even when they already had something that worked when they tried creating a pull request?

Don't focus on the big picture and keeping everything running, but nitpick to the point where their changes "cannot" be merged as they are, due to a lack of approval by you, and where they are technically holding up a release. Don't recognize that technical debt might easily be registered as something that goes into the backlog and can be addressed later, as long as you look at it in sprint planning (or the equivalent), but rather hold everything up over small concerns or nitpicks, which you should blow out of proportion.

Unless it's your code, in which case shove any issues with it into the backlog and never look at it again. You're going to be a hypocrite and do so for your own benefit.

Don't uplift your juniors or mentees. Being positive to others can have a great impact on team morale. Even little things, like using the occasional emoji or thumbs up to show support for others or their code. Don't do that.

Many out there might benefit from listening to their junior developers and being open and encouraging about the suggestions and feedback that they might have to offer. You probably won't want to do that, because you don't really care. Add a few more question marks in code review where possible, use language that sounds accusatory or just blunt, especially because text can be interpreted more negatively than it would seem, or just don't be very helpful. Your average code review comments should look a bit like the following:

code review

(sometimes banter can be okay, but if you cared about not offending anyone, you might want to err on the side of caution)

Essentially, when someone receives comments on a merge/pull request of theirs, these are the things that they might care about:

  • Do I understand what the reasoning behind the suggestion is?
  • Does the suggestion explain what to actually do, clearly?
  • Do I agree with the suggestion and is it viable now?
  • Do the suggestions feel constructive, instead of making me feel miserable?

Clearly, the answer to all of those should be: "No." And the only way to understand what is actually requested should be getting in a call sometime, much like described above. Having messages drop about a code review with about 20-40 comments about "mistakes" is usually demotivating regardless of who you are, especially if the tone makes you feel bad in subtle ways.

It is perhaps a bit surprising, how little effort being encouraging takes, contrast the following examples:

What are you doing in this line???

Wrong. This is bad, you shouldn't do this. Look at existing code instead (with no clear explanation or reference).

I don't like this: (personal opinions) You should change this.

against more neutral language, that's a little bit encouraging and explores the reasoning behind the existing code:

I'd like to learn a bit more about what this bit of code does. Why do things exactly this way, did you consider any alternatives?

I think we do something similar in this other bit of code: (link to code) Maybe we should consider whether that could be a better solution?

I feel like this could be improved because of the following reasons: (personal opinions) Do you agree with these points? Could we address any of them now, or later?

Have lots of meetings. Meetings can be a good way to bring everyone together for some productive brainstorming and have everyone be on the same page about something. Meetings can also be a massive waste of everyone's time, half of which could have been an e-mail or a chat thread, which can also lead nowhere. Do more of the latter.

In the previous century, the United States of America published a document titled "Simple Sabotage Field Manual", which was meant to instruct people in occupied territories on how to act not to help the occupying force. A lot of it focused on doing work badly and inefficiently, slowing everything down. A lot of it is also perfectly applicable to our modern corporate culture.

Here's an excerpt from it:

(11) General Interference with Organizations and Production

(a) Organizations and Conferences

(1) Insist on doing everything through "channels." Never permit short-cuts to be taken in order to expedite decisions.

(2) Make "speeches." Talk as frequently as possible and at great length. Illustrate your "points" by long anecdotes and accounts of personal experiences.

(3) When possible, refer all matters to committees, for "further study and consideration." Attempt to make the committees as large as possible - never less than five.

(4) Bring up irrelevant issues as frequently as possible.

(5) Haggle over precise wordings of communications, minutes, resolutions.

(6) Refer back to matters decided upon at the last meeting and attempt to re-open the question of the advisability of that decision.

(7) Advocate "caution." Be "reasonable" and urge your fellow-conferees to be "reasonable" and avoid haste which might result in embarrassments or difficulties later on.

(8) Be worried about the propriety of any decision - raise the question of whether such action as is contemplated lies within the jurisdiction of the group or whether it might conflict with the policy of some higher echelon.

Some meetings can instead be replaced with an e-mail, or a chat thread or channel in an application of your choice. However, for your needs, meetings are good, because they force everyone to take blocks of time out from their day and are especially useless because many people don't prepare for meetings as much as they should. If you actually cared about the success of the occasional meeting, you should be able to go into a meeting with the mindset of: "Okay, here's a few slides and a little writeup I've prepared, let me explain everything in 10 minutes so we bring people up to speed, then we can focus on solutions and be done shortly afterwards."

Don't do that. Instead, have meetings with vague topics, perhaps with the wrong people in them. Don't have anyone moderate them and let people carried away arguing about small details, much like the aforementioned bike-shedding. Daily standups are the perfect time to argue about variable naming, after all. In general, don't even pay that much attention, merely do whatever you want, possibly with your camera and mic off if it's done remotely. When asked about something, remember to raise up additional concerns for others to waste more time on.

And the best part is that if the meeting isn't recorded or someone isn't writing up notes during it, nobody will remember what was said 2 weeks later anyways. When used correctly, meetings can be a good organizational tool, but it's also easy to do them wrong entirely.

Don't do legacy code, rewrite everything your own way. You will always be slower with code that has existed for a while and that someone else knows better. So ultimately, your goal is to get to a point where you have written the majority of the code that is running, or at least have come up with the majority of its abstractions and mechanisms, that others have to use.

In plain terms: if you didn't write the code that you have to work with, you're already losing. Worse yet, if there is known behavior for the code that isn't entirely tested with a full unit test coverage, you'll occasionally break things and it won't look good. But if you can sell a full rewrite to whoever is in charge, citing any number of past issues, you can put yourself in a way better position. You won't have to struggle with old tools, awkward abstractions or mechanisms written by others and so on.

Instead, you'll be able to lay new ground, explaining that any differences in behavior or breakages are caused by you "doing things better now" and "working in an agile fashion", where anything that's now broken can simply be put in the backlog as a low priority task, to be added later. Get the core of what needs to work right and ignore whatever feels like it wouldn't be fun or nice to implement. Now, to be honest, replacing large and complex monoliths with smaller components that are limited in scope (be it decoupled packages that are a part of the same codebase, or multiple services) can actually have lots of benefits, but only if everyone is on the same page when developing them.

Because of this, you should skip out on the types of activities that could actually be immensely useful in the long run: like cross-functional brainstorming and design meetings, which could otherwise help you resolve issues in a timely manner. In addition, also be sure to skip out on retrospectives, where you can talk about what worked and what didn't work, which would otherwise be a great way to understand how people feel about working in the project and how to make things more comfortable for them. You are writing the new code for yourself, so you aren't slowed down by dated technology, where applicable.

Technology. CV driven development, cloud offerings. Another important aspect is prioritizing your career over anything else. Some say that you should use the right tool for the job, I'd say that you should learn new technologies on company time, even if they're going to be put in production to less than stellar results.

It doesn't matter if Kubernetes shines in larger projects or with competent ops teams, you should definitely use it in your project because that's what's hot right now in the industry and looks good on your CV. Better yet, make someone else host the cluster for you, like one of the managed offerings from AWS, GCP or Azure, which might be more expensive but will let you get all of the experience you need as a end user, without worrying about the long term implications of going for the cloud (and the costs of doing so).

Actually, as long as it won't get you fired or won't make you go to jail, use SaaS and PaaS solutions for everything imaginable. Need a platform for managing logs or metrics? Go with something like DataDog or Sentry. Need analytics? Go for Google Analytics. Need to store files? Use S3 or a similar solution. The same can apply to pretty much any of your infrastructure pieces, ideally you'll even use a managed database instance and make the cloud platform take care of your TLS certificates.

This simply means that you'll pay a lot (read: your company will pay a lot) to make your problems someone else's problems, if at all possible. And if it's not, then consider your options: you'll probably want to have admin access to any platform that you use, given that having to wait for someone else to configure something for you will be a bad look for you, unless you want to blow up their phone with reminders, or tell both your and their boss about how you're blocked because of them not being quick enough.

After all, a little shadow IT never hurt anyone. Don't get me wrong, there are cases where that is indeed an arguably good approach to solving problems, otherwise the cloud services industry wouldn't be booming right now, however there are certain risks associated with using the cloud, that will hopefully be someone else's problem by the time you're in another company. You should run using any cloud service by your info-sec people first, but that's only if you cared about it.

Technology. Infectious abstractions. With enough effort, you can write your code in a way where your over complicated design patterns will be forced upon others, slowing them down a lot, both because they'll need to understand said patterns, as well as because they'll find it hard to add additional changes.

There is something that's called "the rule of three", which posits that not everything needs to be deduplicated:

Two instances of similar code do not require refactoring, but when similar code is used three times, it should be extracted into a new procedure.

Similarly, people have talked of the supposed importance of DRY ("don't repeat yourself") versus something like WET ("write everything twice") for a while now and the average views that people seem to hold have gotten less dogmatic over time. Many believe that removing duplicate code is a good idea, but removing code that is only incidentally the same but might evolve differently will only cause problems. There are also nice little arguments like "You must comment your abstractions" in that second link, which in practice might help people figure out what they're working with.

You'll do the opposite. Take DRY to its maximum and insist that all code is "optimized" and as "good" as possible. In your case, "good" will simply mean a large amount of abstractions that would supposedly help with implementing similar code in the future, but in practice will mean that even relatively simple changes will need to deal with service patterns, inheritance, abstract methods, visitor patterns and basically everything else you could find in a typical enterprise codebase. Better yet, don't comment how it all is supposed to fit together, embrace long names that will eventually seem like line noise and see everyone else get slowed down as they navigate the mess of 40 or so files just to add one new field to your application.

If you are the one who has written the code, then you'll also get the added benefit of actually knowing how it works, at least for a while. Also, have you heard of composition over inheritance? Yeah, forget about that, write the most OOP code that anyone will have ever seen, if you can't see diamond inheritance problems wherever you look, you're not trying hard enough. The end goal here is that you'd be able to navigate your own codebase well enough, but anyone else might have to learn your badly written, badly documented meta-framework and work with all of its idiosyncrasies, so it's a given that you'd be more productive in it than anyone else.

Technology. Database design. If anyone can look at the schema of your database and understand what it's meant for, you're doing something wrong.

When developed with care, most databases will be understandable at a glance. You might not immediately get all of the intricacies, but what links to what, which tables have most of the logic and attention surrounding them should become apparent easily enough when presented with a generated ER diagram of the real thing. Consider this random database schema from a small project I did years ago:

database schema

This particular project used Latvian for naming the tables and entities in the app due to the system and its domain being fully local (which is only an okay choice in a set of very limited circumstances like these), but perhaps that makes it an even better example: even if you don't know the language, you still have an idea of what relates to what due to the usage of foreign keys. There are some projects out there which might want to turn them off for a variety of reasons, but when they're there, they can help you understand everything.

So don't have them. Better yet, add a lot of polymorphism to your system, attempt to put in as much complexity as possible, all under the guise of future proofing. I've seen some database schemas where you needed to jump through a dozen tables just to get some data for what should be a few JOINs. I've seen schemas where you have both OTLT and EAV patterns used as the main way of querying data, and I've seen situations where at the end of those long chains you have a database column that holds JSON data that is used in place of a foreign key, as a polymorphic link.

I've used the now a bit dated article "OTLT and EAV: the two big design mistakes all beginners make" as a cautionary tale before, but in our case you should use it as a manual for what to do. Essentially, the database will make little to no sense without the application, especially if the app will have enumerator values for link types, so discovering how to query data will become a task of digging through existing code and trying to put together what otherwise really shouldn't feel like a puzzle.

Operations. Local databases and environments? Experimentation shouldn't be safe, rather there should be a certain impact to the inevitable failures.

For a second, consider that database above. It was setup in minutes after years of me not touching the project, thanks to Docker Compose and automated migrations with something like dbmate. It's also possible to seed the database with random (yet valid/believable) test data, sometimes even default accounts for local testing. If you don't have that in place, setting up an environment will take anywhere from hours to days, with figuring out what you even need being the first step.

Not only that, but if you choose certain enterprise databases and don't think about local environments from day one, it will be immensely difficult for you to add them later, which had a lot of aspects to consider:

  • for one, latency to wherever the shared instance lives will become an issue, especially if you have lots of DB queries in your app
  • furthermore, any DDL or migrations that might break something will impact all of the team members that are running apps connected to it
  • the data itself might get "ruined" if you don't have mechanisms in place to easily generate things for certain business scenarios

In a past project where I personally ran into similar circumstances I actually ended up going through the whole process of exporting the schema and data as selectively as possible, then fixing everything across dozens of steps so that it would properly fit into a local instance, just so I could test some changes - application page load times went down from close to half a minute to just a few seconds, in addition to it being safe for me to break things or mess around with the data. While I shared the instructions with the team, you probably won't, especially because few people would even know where to start with trying to setup such a local instance, especially with enterprise databases at play.

One can also say the same about any remote service that you cannot run locally, especially if you might care about the logs, which you also might not be able to access in some situations.

Operations. Logging? Discoverability? Some say that your systems should read like an open book when you're working on them. Wrong, it should be a black box.

Some companies out there have more or less made a business from creating technologies geared towards observability, like the aforementioned DataDog and Sentry. It's not that they just promise a lot of features, but they also deliver on them - a lot of the problems are hard to debug and figure out the causes for any number of problems, if all that you have are logs and some vague user reports. These technologies open up the possibility of you being able to look into individual application calls as well, or get a view of how the various services talk with one another, to pinpoint the cause for an issue, be it a service call, a slow DB query, or some badly written application code.

And yet, I've personally talked with lots of developers who haven't even heard of them, or don't use them and are relegated to the loop of:

  • add some additional logging to the application
  • deploy, possibly to production, if not reproducible in staging/test environments
  • hope that the issue is reproduced and that the logs will help you
  • try to write some code for a fix, hope it works

Worse yet, even if the idea of maybe self-hosting Sentry yourself feels daunting (for example, due to project requirements), even simpler solutions like Apache Skywalking get you most of the way there regardless and would be easy to introduce to most projects with popular tech stacks (common languages and frameworks):

Skywalking example

That said, adding all of this instrumentation to your projects would immediately make it easier to discover issues as they are happening, possibly even the code changes in question, rather than allocating an unknown amount of time to fixing even trivial problems. Furthermore, you might end up with an on-call culture if you actually have proper alerting in place, so it might be better to not put in the effort. Plus, anyone who cares about the success of the project will probably find new version releases to be stressful without these reassurances in place, since you won't quite know how well your staging or test environments will perform either, whereas you probably won't care.

Operations. Analytics? Gradual roll-outs and feature flags? Looking at a system and being able to tell what's going on, or understand how it's being used will see you shipping useful features. Don't do that.

It's typically not just a matter of building the thing right, but you also need to build the right thing - which is often whatever your customers will give you money for. Analytics, such as Matomo are easy to set up, easy to run, can respect your users' privacy (and run without cookies) and are a good idea to figure out which parts of your system need additional attention and what your users actually utilize. Unless you'll be able to pick whatever is the highest impact functionality (or give it to someone who'll mess it up in a visible way), there's little reason for you to actually go the extra mile.

What's more, some companies out there do gradual roll-outs and utilize feature flags as well: so that certain groups of functionality could be toggled on or off as needed, or to serve new features to only a subset of users, to see whether any issues pop up. While that can be useful for most teams out there, you probably won't have that in place, especially if you don't have any of the instrumentation mentioned above previously.

Operations. Configuration management? Configuration management should get out of hand as much as possible and be a total mess for everyone.

Managing configuration is a complex topic, not just because you might have to deal with a variety of formats (XML, YAML, TOML, .properties files, environment variables and so on), but also because the configuration values might need to be documented and configurations for local or shared development environment probably need to be available to developers.

When we're talking about cloud environments and things that are publicly accessible, putting such data in your Git repos would be a big no-no, unless you want distributed surprise backups of your data. However, if you don't, nobody will be able to easily launch your services either. The ideal solution is probably having a small tool that, when given some access credentials that are assigned to you, automatically pulls whatever configuration is appropriate for the Git branch that you are on (or whatever you request), however I haven't seen many examples of this.

A more unethical solution is simply not having any valid configuration to begin with, to add to the confusion of any new developers you might have, all while touting that you have great security. A chaotic solution might be committing your configuration values and secrets to Git (if the projects are not public), but in a way that they'll clash whenever you do merges across branches. For example, if you were to merge whatever your test environment branch is into the staging environment branch (or do the equivalent with tags), then all of the sudden anyone trying to launch the services in that branch locally might connect to the wrong environment. Fun for all of the team!

Operations. Code tests? Tests are a great way not only to check that your code works, but document expected behavior! So don't do that and test a bunch of mocks and stubs instead.

Sometimes mocks and stubs are needed, because you want to keep the amount of code and functionality that your tests check small and limited. But of course, you can also take it to its logical absurd - write test code that is so full of mocks that it stops testing the actual application and instead tests some idealized version where related services return whatever you want them for the tests to look green each time. This will not only eventually make tests more useless with time, but will also look good, because you'll be able to write a lot of tests in a short amount of time!

Furthermore, a great way to slow others down is to add expensive operations before they can commit their code. After all, you want everyone's code to be linted and automatically fixed whenever possible, as well as make sure that only valid code gets committed to your version control system - so add even heavy and slow operations like run-all-tests-with-docker.sh in your Git pre-commit hooks. They can actually be nice for quick and small operations, so misuse them to the maximum here.

Operations. Continuous integration and delivery? A good automation setup can make it really easy to deploy changes and run any tests and checks in the background. But being the only one who can release will make things more interesting.

One of your goals is to keep certain procedures shrouded in mystery, or rather to become the "go to person" for them. Someone needs a new release to be delivered or deployed? They definitely should go to you for that, so you can block out the entire day from the calendar for that and muck around with the builds and environments, as opposed to pushing a new tag to Git and seeing everything happen automatically over 10 or so minutes.

CI and CD can work in unison to make development a more pleasant and reassuring process: your feature branch code could be built and tested on every commit that the team members push, letting them know whether something should be changed before it is merged in. Releases and deployments could have a similarly low friction and be largely automated to allow anyone with the correct permissions to do a release, as long as all of the checks pass. But as previously, that wouldn't make them view you as someone who's the only person capable of this.

Build reliance upon yourself. When everyone around you cannot get anything done, you not bothering to work too hard won't be an issue. Create an environment in which nobody else will thrive, which is more than likely to make them less interested in being able to understand the system fully. You might be the "go to" person for most questions, without you things should eventually grind to a halt.

If anyone were to actually follow the "tips" above, then the environment that they'd eventually end up with would not have most people be positive and curious about the system, but rather they'd spend lots of time worrying about every action being brittle, or try to figure out things that should be trivial in most other circumstances.

You're also hardly obligated to point out that your TLS certificates will expire in a week, or set up any sort of monitoring for those that would do the alerts automatically, like Uptime Kuma. Nor are you obligated to have any sort of node monitoring running, like Zabbix, which has lots of useful alerts out of the box, or anything more modern than that, especially if it's not in your job description.

Better yet, schedule a vacation around the time when issues could arise, just to remind people that they need you. Ideally, the bus factor in your team will be small and you won't think too hard about introducing your colleagues to how they can handle most if not all of the common tasks yourself.

So then what? If you get that far and everything is in place, your own life won't be too bad.

Of course, you won't make many friends among your colleagues and will have to jump ship before the technical debt and bad practices will catch up to you, but in most projects you can probably get away with years of sub-par engineering before things grind to a halt. What's also likely to happen, is that in such a project and team, the turnover figures will be higher than average, meaning that you'll usually be dealing with people who don't have too much domain knowledge and thus won't be able to tell exactly what is wrong with the project.

If anything, people might even remember you in a positive light, not realizing that things got worse not because of you leaving, but rather because of the ticking time bomb that you left behind. Then again, that's not really a concern of yours, is it:

gaslight gatekeep overwork

If anything, before then you can consider becoming a contractor and charging the company lots more money for your services, because at that point you're more or less holding them hostage, regardless of whether you're in their direct employ or otherwise.

How to be better

Now, with the satire out of the way, what can we actually learn? At the end of the day, you don't have to be some caricature of an evil and selfish person to make one or even multiple of these mistakes. Sometimes you or the people around you will do these things with no ill intentions, merely due to either not knowing any better, letting their egos get the better of them, or not doing any introspection and not considering these perspectives. This is why having people with a wide variety of experience and views (and even a few stories "from the trenches") and people who are unafraid to speak up can actually be pretty useful.

However, some of the above mindsets, practices or environments absolutely do exist, even if typically only in parts. I've personally seen code that felt so bad and unwelcoming that it tripped me up for a day and made me consider working on a farm instead, because that felt easier than trudging through it. And even after learning how the code worked and some of the reasons why it was the way it was, it didn't feel like it was better in any way due to my newfound understanding. Software development is one of the few careers where you can work directly on making your own developer experience (DX) better, so that you don't have to feel miserable coming to work, which many would do well to remember!

Of course, I am not without fault myself. I've written bad code myself - sometimes due to deadlines, other times because of no elegant ways to solve serious performance issues within the pre-existing architecture. I actually remember once writing a caching mechanism that brought down the loading time for a particular component from around a minute to mere seconds when there was lots of data present. It was also future proofed, so that when a colleague needed to concern themselves with cache invalidation some months later, they could solve their issue in a single line of code due to my foresight.

Seeing that in the code review made me feel like I had chosen the right abstraction - and yet, other aspects of the code that I wrote made me dread it and feel like I've created some sort of an Eldritch mess, due to its complexity and lack of discoverability. I can understand why others would not be vigilant about details like that and would see the complexity of their systems grow out of control. In that regard, I'd like to suggest this lovely talk by Venkat Subramaniam, "Don't Walk Away from Complexity, Run", which maybe will be helpful and validating to you as well!

What I think can also be very helpful to keep in mind is The Boy Scout Rule in Coding, which can be summed up as follows:

Leave it better than you found it

That is essentially what I've been doing from the past few years: from making sure that I write code that is reasonably simple to read, understand, change and even throw away, to gradually adding tools that make sense and support developers in their quest to ship features, usually testing them myself beforehand on various small pilot projects or test runs of sorts. In my current place of work, I've essentially improved a large amount of development aspects, everything from how services are shipped and deployed, to supporting tooling, much like mentioned above.

And when it works, it works really well: I can't remember when the process of shipping a release last failed, I can remember very few cases of CI processes failing and even when it was a fault of the CI configuration, it took a few minutes to fix. I can't remember when environments were last unstable because of improper resource limitations, to the point where even test environments are up over 99% of the time. Just the other week, I was able to find the cause for a production issue in less than half an hour, after looking at the instrumentation that I previously put in place. More or less all of that is also documented and infrastructure as code is also taken advantage of.

I think that anyone needs not just the the ability to experiment, but also to make mistakes. The important part here is the ability to also throw away those experiments and bits of code that don't work, instead of committing to a bad design because of the sunk cost fallacy. Developers should be able to experiment, regardless whether at home or at work, in the form of hackathons or even personal or pilot projects, instead of only working on production software. To be able to choose the right tool for the job, you must know your tools. For example, running into the need to manage backpressure in a production project once it's shipped isn't excellent - whereas if you knew better, maybe you'd be able to recommend something like RabbitMQ during the design phase.

They should have other people to turn to, with whom they can openly and without reservation discuss their approaches and how to maybe do things better. Even if there are disagreements or differences of opinion, those should remain respectful. Of course, they should also reach out beyond the confines of their company or their local job market and attend software conferences, read blogs, listen to podcasts or watch videos of what other people do and learn from those, globally, not just within their own culture.

Summary

At the end of the day, what is needed for that is for everything to be in place: both personal interest, as well as a welcoming and supportive community around any such person, including employers. I would say, that we can only have such an environment if we don't always put ourselves first, but rather care about those around us as well. And if you are not in that sort of an environment and have unethical developers around you, you might just need to look for something new. Sometimes you will be able to convince people of how to do things better, or even let them challenge your own views, but if there are serious differences of opinion, you must be okay with the fact that you cannot change everyone's views, because that's impossible.

It's actually a bit curious, because the delayed release of this blog post more or less coincides with my resignation from my current place of work, due to me wanting to take some time off and work on personal projects, as well as conquer a mountain of programming books (in addition to more boring life stuff, like moving to live in the city), before returning to the job market. That said, I've been reasonably lucky in some regards - the company that I'm quitting from was actually reasonably nice, there weren't people who I'd consider unethical developers (although everyone has their faults, me included) and I could only implement many of the improvements due to the trust that was put in me. I'll probably create a separate blog post that talks a bit more about why I'm quitting and more importantly, what I plan to do during my break, which may involve a series of videos about how to do development in a way that's sustainable without making your life too hard.

However, I have also seen some bad code floating out there and have seen discussions on GitHub and in other places (just a few summary posts, of situations that are still in recent memory) between people in various projects, where neither the tone, nor the conduct was adequate. I've also heard plenty of tales about people who weren't as lucky as I was and I've heard others speaking out about some of the issues that plague software development. I do want to live in a world and work in an industry that's a bit better than that, so I will personally do my best in the years to come, and I invite you to do the same.

By the way, want an affordable VPN or VPS hosting in Europe?
Personally, I use Time4VPS for almost all of my hosting nowadays, including this very site and my homepage!
(affiliate link so I get discounts from signups; I sometimes recommend it to other people, so also put a link here)
Maybe you want to donate some money to keep this blog going?
If you'd like to support me, you can send me a donation through PayPal. There won't be paywalls for the content I make, but my schedule isn't predictable enough for Patreon either. If you like my blog, feel free to throw enough money for coffee my way!