The Life & Death Of Software
Table of Contents:
Our team has had an insanely productive past three months. We’ve consistently cruised past goals, exceeded expectations, delivered new functionality, and had fun doing so. How did we do it? How can we keep the momentum going? I’ve been dwelling on these questions over the past few months, and I’ve also posed some of these questions to teammates. If you read Mob Programming & Deep Work, some of these answers may not come as a big surprise. If you haven’t, this article might be a good jumping off point for you as we ponder, together, the relationship between healthy teams and performance in the wild & wonderful world of software.
Here’s what it comes down to: teams perform well when successes are shared and failures are owned. We’ve had some great reasons to celebrate on our team:
- our Product Owner was promoted — a much deserved piece of recognition after a tremendous amount of work (with clearly tangible results) on their part
- Jonathan's Nebula Logger project hit 200 stars (⭐⭐⭐⭐⭐) on the Nebula Logger GitHub. This time last year, Nebula was hovering around 50 stars after Jonathan had already put years of effort into the project. It’s been incredible to see the community in general suddenly realize the massive value the tool presents to both admins and developers!
- We’ve gotten to see Nouzhan truly come into his own as an engineer. Getting up to speed on a team with 10+ applications isn’t just difficult — it’s intimidating — but Nouzhan has thrived in the past 6 months, quickly getting acquianted with complicated pieces of logic while contributing more and more. The worst thing in software engineering is somebody who’s afraid to say “I don’t know” — over time, that fear turns into insecurity and makes the impostor phenomena real. Nouzhan has literally given himself the opportunity to learn by asking great questions — and we’ve learned from him, in turn. (Since publishing this article, Nouzhan has also been promoted from Jr -> Mid engineer; it’s been incredible to see him progress and to be recognized for that progression!)
- (Added since publishing) I was also promoted from Sr -> Lead engineer, with many thanks to my team for their support in the process!
We don’t celebrate successes enough, and part of that (I think) is caused by the feeling of pressure that comes from working on a project that seems dooomed to fail without meticulous and ever-present attention.
Delving Into Engineering Failures
In eight years of software engineering and working with software engineers, I’ve still seen more projects fail than I’ve seen succeed. Work in the industry long enough and you’re bound to be part of a failure — sometimes those are outside of our control, like a startup that burns through their cash runway. More often than not, though, projects fail slowly. Let’s review why.
The Dangers Of Brightsizing
Collective code ownership goes a long way, but there are other threats when it comes to the long-term health of projects — one of which is brightsizing. This term, which I was introduced to recently, is part of the problem. Let’s look at the dictionary definition:
Giving redundancy to the hardest-working / most talented employees of a company
A lot of definitions out there includes an example involving those employees being fired, but pre-pandemic, I would argue that the most common form of brightsizing was instead stagnation — refusal to recognize and share the achievements of individuals or groups of individuals as befits their performance. Many companies had little to no incentive (moral imperatives not really tipping the scales, unfortunately) to retain staff or to unduly reward some in an experience bracket over others — regardless of performance. Labor, in many traditional views, was merely an input — all else being equal, if a person left, another one could readily be found and “plugged in” as an input.
The pandemic, as with so many things, accelerated a process that was already slowly underway — the recognition that people aren’t alike, and that to truly “replace” talented and knowledgeable professionals frequently takes years of time. Brightsizing — if not by name, then certainly by symptom — has been a much-talked about part of corporate staffing for years, and now more than ever with “The Great Resignation” happening (especially in tech)1. In other words … losing your most talented employees is one of the reasons that software projects fail. I’ve seen more projects than I really care to remember fail over the last 8 years for this exact reason.
It would be easy to conclude that things like poor architecture and code quality kill projects, but I would posit that in reality, those are symptoms of brightsizing. To put it another way — if you can retain and reward talented people, you won’t be dealing with things like poor architecture and code quality. Some of you may remember from The Tao Of Apex an infamous commit by a “tech lead” that I refactored from this:
const removeLastNCharacters = (string: string, number: number) => {
var stringToRemove = new RegExp(".{" + number + "}$", "i");
return string.replace(stringToRemove, "");
};
To this (after adding a test, of course):
const removeLastNCharacters = (string: string, number: number) =>
string.slice(0, string.length - number);
I want to be clear: the first version of this function was ripped straight from an overly complicated stack overflow post. There’s a time and place for searching the internet for answers (though I’m of the strong opinion that even the simplest code taken from something like stack overflow should carry a citation in the code), but your organization has a huge problem if the “tech lead” doesn’t know the base library functions of a language.
There are many problems that are the result of brightsizing, but I’ll highlight two in particular:
- if your attitude is “I wrote it, what could be wrong with it?” (what happened in the code example above), the people that work under you aren’t set up for success
- if the people most likely to help a project succeed leave, you’re left with a pool of people that also aren’t set up to succeed (missing crucial organizational / architectural context), who are either incapable of making incremental progress or are incentivized to wash their hands of the “problem” software by starting over — often with disastrous results
I’ll give another TypeScript code example to help clarify the difference between those two points (since there can certainly be crossover amongst the two, sadly). Five years ago, after many months of administrative red tape was cleared, I was given access to another department’s repository. As was often the case while working this job, it had been made condescendingly clear to me that this was a “simple change” — but it couldn’t be done for months by the original team since only one other person knew anything about the application and they were “busy.” Off to a great start, obviously. All I had to do was add a property to an object …
// this should have been a type, not an interface
// but hey, let's pick on one thing at at time, right?
interface ImageProps = {
path: string;
title: string;
// my new awesome property!!
label?: string;
// etc ...
}
// the question mark indicates a property is nullable,
// which would later on allow us to write something like ...
const createProps = (path: string, title: string, label?: string) : ImageProps => {
const imageProps = {
path,
title,
label: label ?? title // fallback to title when label not provided
// etc ... other assignments
};
return imageProps;
}
Except … when I went to access the new label
property elsewhere, TypeScript lovingly informed me that the property wasn’t available. Wait … what? I’d just added it to the interface! It was then that I saw this, repeated in every file where ImageProps
was used:
interface ImageProps = {
path: string;
title: string;
}
Brightsizing is a very real problem for organizations that refuse to acknowledge the worth that talented, hard-working people bring to the table. Code bloat, “copy pasta”, bad architecture … etc, start slow, but the rot quickly spreads. What should have been a two line change for me (one, to add the property to the interface; two, to assign the newly created label
property correctly to <img>
elements being created) ended up being 100+ lines across dozens of files. There was no standardization; there was no sanity. I floated the suggestion during code review that the offending interface be centralized (along with the rest of the duplicated code I’d had to wade through), as well as the creation of the img
tag, and was laughed off. I left shortly thereafter.
That company has a questionable HR history, with brightsizing part of their retention strategy across the board. It’s expected that the average employee will last a little less than two years there. People approaching that benchmark are (with some notable exceptions, of course) hastened out the door. Despite the overall trend I’ve written about above — the recognition that people aren’t alike — there will always be companies lagging behind (hence “The Great Resignation”!). In some ways, the worst part is hearing what the people left behind have somehow managed to do.
I like to compare it to my first org (AKA “the one that failed”), where we were sunsetting a 20+ year old custom built CRM (which cost tens of millions of dollars over its lifespan). They’ve decided (at an executive level) after only a few years of using the system that re-staffing up a team to build a custom CRM would be more cost-effective than tackling organizational debt reflected in the existing automations. “Hubris” is a good starting off point when considering how to describe the rationale behind trying to staff up and maintain a product that has nothing to do with your core business; of course, there will always be companies like Hubspot that actually do this, but the key difference there is that a Hubspot was looking to pivot into an entirely new market — to sell the end result of their labor. They (HubSpot) also pay a competitive salary and have been, in part, responsible for the resurgence in startup culture in the Greater Boston area — those two things are related! If their only reason for creating a CRM of their own was to support their internal business(es), I have my doubts that anybody with an actual eye for PNL would have be able to stomach the long-term costs.
The effects of brightsizing aren’t always immediately obvious, particularly if you’re a new employee joining a pre-existing organization. Look for code smells, tightly coupled architecture, flapping tests, and lack of organizational knowledge (sometimes accomplished through apathy, and sometimes through an overzealous “guarding” of the people working in tech from interacting with other business units — either way, projects coming out of such a place are doomed to fail). These are the after effects of brightsizing. This is also why it’s extremely important, while interviewing, to get a solid understanding from the people who are interviewing you about what they enjoy about their company; in other words, you don’t want to onboard yourself to a team filled with people who hate their jobs and are expecting you to pick up the slack caused by other people leaving. Interviewing for software engineering is another vast topic, so I’ll leave off here on that front.
Code Review Do’s & Don’t(s)
I recently read Accelerate: Building and Scaling High Performing Technology Organizations, and one of the biggest issues that unhealthy & unproductive teams have is a needlessly complicated review process. In reflecting on the reviews that we do now — yes, we do both review code while mob programming and when reviewing a change list / pull request — and some of the review processes I’ve seen, it’s clear that how a review is done is just as important as what is being reviewed. This is where non-violent communication principles can really shine (for more on this, please see Building Better Teams). Note that these are the prerequisite for what follows; any mental/verbal hostility in a review process, and the whole thing falls apart. A huge shoutout to my wife, as well. Her work as a coach has proven instrumental in exposing me to things like Nonviolent Communication — it’s amazing how naming and cataloging these things can help to make sense of how powerful they are as tools!
Bad practices can easily bog down the process; “Accelerate” does an interesting job of showing how having no change review process at all is actually better than having a bad change review process, but I don’t think it’s a big stretch to say that meeting somewhere in the middle is the happy ideal.
Code review is a chance for people — and teams — to get into alignment. Sometimes the person you’re looking to get into alignment with is a past version of yourself; I code review my own open source work frequently, and even features that come and go in a single day still frequently get modified at the point of review. There’s a reason that “hindsight is 20/20” — some things are just easier to catch in review than in the moment. Going back to mob programming for a second, one of my favorite things about it is how the whole process of receiving feedback is “shifted left”. We were recently working on a feature with beautifully decoupled architecture, but finalizing some tests that had broken when we’d begun refactoring was proving painful. Jonathan was able to call out that we’d descended far too deep into the Jest API for our own good, and that the benefits we might receive from what we’d decoupled was now looking less and less certain if we couldn’t get the existing tests working again. Tightening up that feedback loop saved us hours — if not days — of time, and allowed us to revert back to the working version of our code and tests much more quickly.
With all that being said, here’s a few key questions when it comes to code reviews that are succesful:
- would there be a problem if this code was deployed as-is?
- is this consistent with the employed patterns elsewhere, or can it stand on its own as a useful contribution, precedent-wise?
- would constructive feedback on something within this CL/PR work as a learning opportunity for the person submitting?
Almost anything else is a nit, and nits kill code reviews, waste time, and (in general) make people lose faith in the process. It should be something that people look forward to. Don’t nit!
Code reviews get a bad rap — Jessica Kerr was recently on the Changelog podcast talking about how much she hates them, and I think a big reason going through a changelist or pull request leads to a sense of dread is because of how reviewers tend to introduce friction in this process (more on that in the next section). I’m reminded of an open source contribution I made at the end of 2019. The maintainer of the library I was trying to contribute to led off with some feedback; some of it felt more or less arbitrary, but in an effort to maintain goodwill I integrated their suggestions. I was then met with another round of suggestions. The first two times this happened, I was more or less willing to suspend disbelief around what appeared to be completely random nits. By the third round of suggestions, it had become clear: this person was deriving some kind of sick pleasure from lazily dispensing with random pieces of advice. By the fourth round, Kent C. Dodds 2 had been summoned (and, much to my amusement, summarily excused himself with obvious disinterest). After the fourth round of feedback, the maintainer pushed a branch and merged it to main, which not only completely changed the folder structure of the original project, it also made direct changes to the files that I had been working on. One painful rebase later … and several additional rounds of feedback 3 … and my PR was finally merged. That’s why people hate code reviews. Don’t be that person. Don’t introduce needless friction to a process.
Lastly, an anecdote on something that I now consider a nit, but was a mainstay for years in terms of reviewing code. When I left the second org I was with, a friend confided to me that our codebase was the cleanest they’d ever seen. That comment has stuck with me through the years, and in particular I think of some of the things we were wrong about; the areas we still could have improved upon. One of those was our style guide. It pains me to think about the amount of time I used to spend formatting code to conform to our style guide (and I wrote the thing!). Each of us on the team had our own little quirks, and even though we practiced pair programming almost exclusively, we still had to check our own unconscious writing styles prior to checking things in. Style guides, especially in the Salesforce ecosystem, have outlived their usefulness. In our ecosystem, the answer is simple: Prettier Apex. Removing how code looks from what “needs” to happen in a review has saved a ton of time and prevented countless discussions on formatting semantics over the past few years — I could never go back. If you’re reveiwing code and have an issue with the way that it’s formatted, you’re either looking at an opportunity to automate the running of a code formatter, or it’s a nit. Only the former is valid!
Friction -> Burnout -> Disaster
Responding to trouble, and changing practices effectively to remove trouble at the first sight of friction is the single best thing you can do — as any member of a team, from manager to the greenest of contributors. Otherwise, friction leads to burnout — and burnout, to disaster. But what do I mean when I use the word “friction” ?
Here are some examples of friction:
- onerous code reviews (the one above is an extreme example)
- difficulty performing deployments
- can also manifest as “deployments can only be done by one person” or
- deployments are associated with regressions, and the fear of being blamed for them
- corporate values that don’t encourage taking time off
- corporate values that differ from your own values
- management so bad that the values don’t come into play in your life
- bad work/life balance, in general
- frequently a sign of toxicity in a workplace
- siloed nature of work
- frequently coupled with poor visibility (or non-existent structure) for career next steps
- unrealistic estimations/timelines
Here are some examples of removing friction:
- code reviews that focus purely on necessary cleanup / architectural insight
- CI/CD
- blameless post-mortems (I talk more about this in A Tale Of Two Scope Creeps)
- unlimited PTO (or a highly generous PTO allowance) and the encouragement to use it (5-6 weeks a year is a good starting point)
- good scheduling, with consistent start/end times
- highly visible, well-understood promotion cycles and goals towards advancement
- having a highly supportive team
- revising timelines with new information 4
- kaizen-focus, or “making things easier to maintain”, as a friend would say
Something that I’ve encountered frequently on the side of introducing friction? People that assume engineers want hard challenges and to always work with cutting-edge technology. I would hazard to guess that most engineers, if asked about what’s important to them in their work, would begin with items from the second list. Engineers are people first. If every task is wrapped up in this voodoo mentality of having to be difficult, burnout inevitably sets in. If everything needs to use cutting-edge technology, what that really means is that you are about to spend a ton of time finding undocumented bugs, feature disparity between existing tools, and the need for effective boundaries to silo off your new infrastructure/platform/code until it can stabilize.
I have seen teams and people quickly iterate, employ cutting-edge tech to solve today’s problems, and have a great time wrestling with thorny issues, but I believe the ability to do those things is predicated on working in a low-friction environment. If you feel cut off from your company or team’s culture, disconnected from what you’re working towards/on, don’t have visibility into career advancement opportunities, etc … it’s much less likely that you will be to withstand the rigors of day-to-day work, let alone long-term, sustainable, project success.
Returning to “the one that failed” project, from a different angle:
management so bad that the values don’t come into play in your life
Is frequently offset by:
having a highly supportive team
In a “toxic-enough” environment, it’s not unusual for team members to band together; to support one another through shared hardships. That kind of structure, like the lattices formed by snowflakes, can’t be maintained for long … and when it breaks, it’s the HR equivalent of a dam bursting. This is precisely what happened with “the one that failed” — our two most talented engineers left (Jonathan, for a sweet consulting gig in Mallorca, and our other friend for a high-paying job with Wayfair). Within six months the rest of the original team (myself included) had left. Jonathan and I kept in touch over the years, and now have worked at three companies together. It’s no coincidence that we’re the ones that reunited — and not us with our former bosses. On the other hand, I told my wife about a dream I had a few months ago where I was offered an exorbitant amount of money to return to that first job to fix their CRM implementation. Even in the dream, with the possibility of early retirement on the horizon with the kind of money they were talking about, I couldn’t accept. Bad management, and other sources of friction, burn bridges.
Known Predictors For Project Success(es)
So what makes engineering teams great, and what keeps high-performing teams consistently healthy and fast?
High Performing Teams Have Fun
Toward the end of my consulting with Publicis Sapient, I was put onto a project with a few other people. Though I’d already accepted my offer with Salesforce, and was excited to start, during my brief tenure with this team I caught a glimpse of what I’d been missing out on as the solo developer on a great deal of projects. Despite the pandemic and remote work, these people had fun together. When my initial foray into their team (to do something similar to One Small Change) ended with me talking to them about their existing tech debt, the project manager called me shortly thereafter.
- “Hey, remember how you were going to do just do this one small thing for us?”
- “Yeah, why?”
- “I’ve gotten approval for you to work with us as much as you’d like until your last day.”
- ”… so I can help fix all these problems I was just pointing out?”
- “You bet. Go nuts.”
Those coworkers went so far as to throw me a virtual happy hour when I left, and we’re still in touch. I consider myself lucky for having had the chance to work with them; not every consulting project is like that, and (in particular), not every team has enough trust in each other to respond well to bad news (like a “small change” being impossible in the existing architecture). Teams that have fun together — and can welcome newcomers gracefully into their midst — are more resilient and more productive.
The Importance Of Trust
“Trust” is a word I’ve reflected on with increasing regularity since starting at Salesforce (it’s our #1 core value). It’s a simple word that means many things to a lot of different people, but principally I’ve found that teams that trust each other stick together, have fun, practice non-violent communication, and outperform other teams. Trust can be found in many different forms:
- in the willingness to be vulnerable, and to admit that you don’t know something
- in the ability to hear somebody out fully, without interruption
- in the practicing of new skill acquisition (learning mindset) — trust that time invested is time spent well, in other words
- a distinct lack of micromanagement
- in a collaborative working environment (more on this in a bit), trust and the absence of micromanagement assume an even larger role in the workflows of healthy teams
The list could go on and on. Trust is the exact opposite of friction: it allows people to work together like a well-oiled machine by reducing the complexity of social interactions down to what’s necessary. It allows for the existence of levity — another well known social lubricant — and the ability to celebrate others (which can be contrasted with what we see in toxic team cultures: the jealous guarding of accomplishment for fear of being passed over for advancement).
It’s trust in a team or person that allows a business to hand off the responsibility for automation. Successful teams can be made up of people with many differences, so long as they trust one another and can put those differences aside at work. This brings to mind an old coworker of mine — somebody who couldn’t have been further apart from me in personality & interests outside of work. Despite this, they were incredible to work with — a paradigm vastly preferable to its inverse (where you like somebody personally but can’t stand working with them). Such teams aren’t simply composed of similar, like-minded people; they can be made up of people worldwide, united toward some common goal. This is where feeling that shared purpose and having an alignment with (at least some subset of) corporate values actually matters, as it’s one of the ways high-performing teams stay motivated, even as:
- requirements morph
- the backlog grows
- bugs need to be fixed
- we all face huge changes in work & life
Purpose is a lofty goal to work toward; the belief that we’ll get there is trust.
The Importance Of Mentorship
Proper mentorship contributes in an ancillary fashion to successful projects, by enabling the proper transition of knowledge between newcomers and the more battle-seasoned. In practice, good mentorship is also bidirectional, in that it’s a learning experience for both parties. When I was first being mentored as an engineer, I was doubly fortunate: I had a great teacher, and I didn’t have to go looking for that teacher. People’s paths to being a mentor or being a mentee will undoubtedly vary greatly; for me, I needed a mentor and didn’t know it. This is an important and seldomly-spoken about part of the process — some people know to raise their hand and ask for help, and others need to be helped along (or have the process simply be in place to guide them, without them having to ask for it). Organizations that provide paths to technical mentorship are more resilient: their teams have a greater capacity for institutional knowledge being shared.
Crucially, mentorship differs drastically from management, and it’s not always a good idea for a person’s manager to also be their mentor. There can be issues on both sides of the managemen chain when it comes to that, because a mentor’s role is different from that of a manager’s. A mentor walks through concepts and reviews cases in applications and the code that serve to highlight those concepts with a mentee. When a manager has the time and the wherewithal to also be a mentor, and the mentee is open to one person serving in both roles, it can work out that one person performs both duties, but coaching — a field still in its nascency, particularly technical coaching — and managing serve different roles and not every manager is well-suited towards also coaching their individual contributors. Likewise, not every IC is well-suited to being coached by their manager.
There’s a curious dichotomy in the learning process, as well: we learn what not to do either through the “stove” method (you burn yourself once, either by introducing a bug or by writing code that doesn’t do what you think it’s going to) or through observing incompatibilities between ourselves and others. Mentorship allows us to travel more quickly down the path of what to do by providing us with key insights into the critical thinking behind decisions — shortcuts here are typically heuristics brought on by experience!
Having the chance to mentor several excellent junior engineers was something I really appreciated at Publicis Sapient, and that the importance of mentorship was stressed there without regards to where in the management chain everybody was. I’ve always loved pair and ensemble programming because they both wrap mentorship into the collaborative process. We succeed as a team because the shared process of learning gives us changes to go on beneficial tangents when there’s an interesting pattern or piece of software architecture interesting to explore; and, again, I want to stress that mentorship is a two-way street where all parties benefit. Here’s an example from a recent bit of pair programming Nouzhan and I did (I’ve generalized this example to TypeScript, but otherwise the intent of the original code is unchanged):
type Data = {
fieldOne: string;
fieldTwo: string;
// etc, other properties
};
type DataOverrideConfig = {
fieldOne?: string;
fieldTwo?: string;
// etc, other properties
};
const updateBasedOnConfig = (data: Data, config: DataOverrideConfig) => {
data.fieldOne = config.fieldOne ? config.fieldOne : data.fieldOne;
data.fieldTwo = config.fieldTwo ? config.fieldTwo : data.fieldTwo;
// etc ...
};
Nouzhan and I had stumbled across this code completely by accident while consolidating several different code paths into one distinct class which dealt with configuration changes to data. Other than moving the functions to live within the same class, we hadn’t intended to change the nature of the code at all. However, in looking at the two ternaries above, I had to stifle a laugh, and took this as a chance to show how we could change the code to allow for a more meaningful reading experience:
const updateBasedOnConfig = (data: Data, config: DataOverrideConfig) => {
if (config.fieldOne) {
data.fieldOne = config.fieldOne;
}
if (config.fieldTwo) {
data.fieldTwo = config.fieldTwo;
}
// etc ...
};
Note that the original language wasn’t JavaScript (otherwise there would be more idiomatic methods for accomplishing the same thing); principally, the change was about updating the way that the code read to avoid having to unnecessarily write the same field value when it didn’t exist on the override type.
- With the ternary, the code read as: “always set this property, even if the config override doesn’t exist and the value is the initial value of the property itself”.
- With the if statement, the code reads as: “if the config override contains a matching property, use it to override the data’s existing value”.
The latter is much more in line with what you might imagine as the business rules being read to you, and it was worth a few minutes of our time to review why making the code more readable was a net benefit to everyone.
Over time, proper mentorship can drastically accelerate a mentee’s capacity and ability to self-service in their own learning, and I’d be remiss without thanking the mentors I’ve been lucky enough to have had in my own career: a huge thanks to Jonathan Gillespie, Bruce Lindsay, Manhar Trivedi, Jeff Bennett and Stuart Blair.
It’s Personal: Pandemic Collaboration
One of my goals when writing about Mob Programming for the Salesforce Engineering Blog was to show how important strong collaboration skills have been for teams weathering huge shifts in their work & personal lives due to the pandemic. The silver lining of the pandemic for many workers in tech has been the unprecedented demand for people with their skillset, coupled with the growing realization by companies that remote workers aren’t just capable of doing their job anywhere — it’s also that the talent pool has now grown several-fold by no longer requiring workers to live/move close to an office. And, of course, amidst all of that is something that’s reminiscent of the American Dream — the realization that with proximity now less of a factor in being gainfully employed, it’s possible to live outside of cities, or in smaller cities with a better cost of living, without having to be retired first. I know many people who have taken stock of their surroundings during the pandemic, and found them wanting — in many ways, this attention to “minor” grievances in lifestyle and work (which might have otherwise been ignored for years/decades) has been the result in massive changes within the workforce. Working for an organization that prioritizes its people over their locations is a massive quality of life improvement that I would recommend to anybody — it’s what’s allowed:
- Nouzhan to imagine a future back in his home state of Georgia
- Jonathan to move out of the Bay Area without losing his job
Any time workers have agency in their lives — the freedom to make decisions (even if that decision is: “I won’t be afraid to dream about the future I want”) — they’re going to be more motivated, more personable, and more likely to want to continue to succeed. Going back to the book “Accelerate”, there’s quite a bit of science to back this up (and you’ll note the existence of the word trust in this next quote):
… stocks of companies with a high-trust work environment outperformed market indexes by a factor of three … 5
If you’re engaged, kept happy, and empowered to do your best work, everybody benefits. If you’ve made it this far in this article, none of that should come as any surprise. But a funny thing happened on the way to normalizing life within the pandemic — medical professionals weren’t the only ones completely slammed. Mental health professionals reported unprecedented demand for their services (I talk about this in Mob Programming & Deep Work as well) as the combined effects of workplace friction and household psychology collided. This is an ongoing change, and it’s just one among many that are currently permeating our shared culture and values at this very moment. All of that is to say: we don’t know or have the ability to fully comprehend the way that the pandemic has shifted workplace culture and our own lives. What we do know is this:
- “Zoom fatigue” (a phrase that didn’t exist within our cultural gestalt a mere two years ago) is talked about everywhere
- While many software developers prize working alone, they’ve also been a huge part of the “Great Resignation” (see footnote #1, again)
Those two points are related, to me. We’re less likely than ever to “put up with” discrepancies between corporate values and our own. We’re more impatient than ever to have the chance to do work (and prioritize/enjoy meetings less as a result), but we’re more picky about the work that we’re doing, too. Couple that with a greater feeling of being disconnected, and it’s easy to see why people are leaving jobs in record numbers.
This is where positive screen time comes into play. In four months, it will have been 6 years since Jonathan and I saw each other last in person. At our last company, we spoke infrequently; we were working on different projects and rarely had the chance to catch up. We collaborated within open source, particularly within Nebula Logger — even though we were, nominally, coworkers, it was hard to keep in touch! Since we both joined Salesforce, we’ve mob programmed on a video call every day (all else being equal, vacation, time off, etc …). It’s been an incredible experience. I’ve never met any other member of my team — but it doesn’t feel that way. Though we practice ensemble programming for 6+ hours every day, the atmosphere in the “room” is laugh-filled and smile-ridden. It feels good.
That feeling is important, and it builds on trust. Yes, we’ve had a number of successful launches — since I first started writing this article, we actually launched an entirely new project within the confines of a single sprint. It was stressful, at times, and we had to do a hotfix the next business day in order to get everything working correctly. At this point in my life, I can’t do that kind of work constantly — it’s too stressful — but I’m happy to be on a team where we trust another enough to say: “we launched two big projects in two sprints and at the end I was starting to burn out — what can we do to avoid that, going forwards?” Strong, collaborative teams can have conversations that might otherwise be difficult, and they can have them without the kind of ceremonies — setting up a meeting, making sure everybody’s availability is fine, breaking the ice since people haven’t spoken as a group in a while, etc … — that frequently go into the same kind of conversation on teams where everybody “being in the same room” isn’t the norm.
Kaizen Focus
Going back to the reducing friction list, you may recall I referred to “Kaizen Focus” at the end; for those not familiar with the original Japanese term (改善), kaizen is the process of continual improvement. This is something that Agile, as a framework, picked up from the Japanese manufacturing industry, and in particular, it’s become a common practice when “implementing Agile” to have teams identify — once per sprint — a kaizen item that will help them move faster in future sprints. The friend I mentioned above — who was ruminating on how we spend more time maintaining code than writing it, which feeds into why making code easier to maintain through something like a kaizen is so important — referred me to The Noble Art Of Maintenance Programming while were talking about this, and I think these two ideas:
- Once you’ve written something, it’s immediately in maintenance mode (sometimes expressed as “tech debt isn’t just code you’ve inherited - it’s also the code you’ve just written”)
- Reading code is harder than writing code
Go hand in hand when we consider what differentiates successful projects from failures. Successful projects aren’t written brilliantly to begin with, and then never touched again. Rather, like a painting, they go through many, many iterations after the first broad strikes. Put another way — successful projects are well-maintained. In some cases, they’re meticulously maintained; in others, they’re maintained by rote of “having to be there” (you go in to make a change and all of the sudden the opportunity to refactor something presents itself …). There’s an odd balance that happens on this front, when it comes to teams, and this is one of the more “lucky” parts of engineering — ideally, you want to land on a team where the vast majority of people are passionate about producing quality code, regardless of whether or not that code is new or old. I know I said I wouldn’t go into the weeds with interviewing, but as a candidate, being able to deduce what the capacity for tending to older code is and how teammates (if they exist) feel personally about their own roles as code stewards is incredibly important.
In The End
I’ve been writing this article for about a month now, chipping away slowly at sections of it as time has allowed, and reflecting a lot on the positive and negative experiences in my career thus far. I think we all, to some extent, are looking to “be the change we want to see,” but that can be exhausting when working for the wrong company, project, and/or team. It’s my hope that if you’ve made it this far, I’ve given you some helpful tidbits to think about when it comes to answering the question:
How can I improve the quality of code I or my team produces?
This is meant to be a northstar for engineering managers, healthy food for thought for individual contributors, and inspiration for those starting off their software engineering journeys. It’s meant to start a conversation; to be a living document (if at all possible) for consolidating the things we get right and the things we get wrong when it comes to software engineering. And, bringing it back to the very beginning — shortly after publishing this article, Nouzhan was promoted (Jr. -> Mid), and I was promoted (from Senior -> Lead) as well. It feels good to be working for an organization where success is rewarded, and getting from point A to point B is clearly delineated.
If you enjoyed this article, chances are you might also find The Tao Of Apex interesting, as that’s also a reflection piece on some of the more abstract pieces of software development and their relationship to time-tested philosophy. I also wrote about how to Uplevel Your Engineering Skills with reflections on the most useful soft skills I’ve learned as an engineer. Thanks for reading — all the best to you in your own journey.
-
Dominus, Susan. "Tech Companies Face a Fresh Crisis: Hiring." The New York Times. Accessed 1 March 2022
↩ go back from whence you came! -
A big name in the React community, but if you're interested in reading about React, I would recommend Dan Abramov's blog over anybody selling you their expertise as a service ...
↩ go back from whence you came! -
I went back and looked at the PR after writing this. All told, there were 22 different rounds of feedback over the course of two days, including several suggestions that, if I had accepted them verbatim, would not have worked. Don't be that person! If you have an open source project and your project's contributing guidelines don't include things like "following a hidden style guide," the best thing you can do when receiving a PR from the community is accept it, thank the people responsible, and do whatever sort of minor fixups you'd like afterwards. Don't nit.
↩ go back from whence you came! -
e.g. "Responding to change over following a plan". Beck et al. "Manifesto for Agile Software Development".
↩ go back from whence you came! -
Forsgren, Humble and Kim. "Accelerate", 103
↩ go back from whence you came!