Lost in Process
Agility
At first glance, this might look like a tirade against Agile, but let’s set the record straight: the goal here isn’t to demonize the current state of things. I’ve been fortunate to work on projects where recent Agile principles were applied with genuine intent, creating dynamic environments that nearly reach the ideal. These cases aren’t unicorns, but they’re not the majority, either. In this article, the focus is less on those genuine successes and more on the other side of the scale, where Agile has become a hollow ritual, weighed down by bureaucracy, losing sight of the values it set out to uphold.
The Agile Manifesto offered a simple and clear set of intentions; empowering teams to respond swiftly to change, prioritizing collaboration over rigid processes. Yet, Agile today has often become a shadow of this original vision, appropriated to justify routines far removed from its founding principles. Meetings stack on top of meetings, scrum, stand-ups, retros, all too often leaving developers in sessions for the sake of attendance. In some teams, members even rotate roles weekly, taking on quasi-Scrum Master duties to lead sessions that lack meaningful exchange or creative purpose, adding yet another layer of distraction. In a setup that ostensibly gives team members more control over the final product, all has become an exercise in ceremony rather than substance, where 'being Agile' means, sometimes, color-painting reports and duplicating data from specialized tools into summaries, even though it’s readily accessible in customized dashboards, all while staying on-call to monitor the ever-active alerts channel, be thankful if those alerts are actually coming from work you’ve developed. Then, in the gaps between these busy timeslots, developers manage to squeeze in bits of coding, just enough to implement a feature but with little room for cleanliness or experimentation. In this scheme, “leave the camp better than you found it” is a distant ideal, overshadowed by a system that discourages both quality and innovation.
There was a time when agile didn’t require a manifesto, it was organic and entirely natural. Teams worked in dedicated roles, with some members focused on development, others on architecture, infrastructure, or functional work like requirements gathering and task definition. Each person could immerse themselves in their primary responsibilities, unencumbered by constant context-switching or procedural demands, with ample creative time to refine their work. While many held coding or technical interests beyond their work roles, during the workday, responsibilities aligned with professional strengths and preferences. And if interests evolved, transitioning into new roles was always possible. This environment, which didn’t need to remind us how agile it was, was more purposeful and infinitely more respectful of the craft than the rigidity often found in Agile™ today, an approach that constantly sells itself as dynamic, open, and liberating, yet often feels anything but.
In many cases, Agile has turned developers into multitaskers juggling numerous responsibilities but excelling in none. Developers may be given access to every phase of a project, from planning to post-mortem, but too often lack real influence over the most critical part: implementing the code with precision and care. Rather than supporting deep, skilled attention, this version of Agile scatters concentration across meetings, reports, and rituals, leaving little room for thoughtful effort. The result is an approach that values surface involvement over dedicated, professional contribution, making quality-centered time an afterthought.
Code
In a field where being called a 'Software Engineer' has become the norm, I take a certain pride in simply calling myself a developer. I enjoy the full scope: creating, architecting, deploying, and scaling my code, building the infrastructure that allows it to thrive. But here, I want to center on the heart of it all: the code itself. There’s something grounding about identifying as a developer, a reminder that at the core of all these responsibilities lies the craft of building. In this particular yell-to-the-sky, it’s the code, its quality, its care, and its craft that I want to shine a light on.
A colleague once remarked that there’s no greater satisfaction than walking away from a project riddled with spaghetti code. It’s a sentiment many can relate to: encountering code that’s chaotic, inconsistently updated, and often the result of rushed or careless work. Sometimes it’s not even the code itself that’s offensive; it can be corrected, though it’s often treated as a pointless chore. It’s the evident lack of care behind it. In such environments, clean code is the last link in a chain of rituals, overshadowed by other, more ceremonial concerns. If nobody cares about clean code in an organization, the problem is bound to grow worse over time. Perfection need not be the enemy of good, but when quality is dismissed altogether, the rot sets in.
Some argue that “clean code” is arbitrary, just a matter of preference. They’ll say if you ask ten software engineers, you’ll get multiple camps with differing opinions. But I disagree. Give those ten engineers enough experience, whether measured in title, years in the arena, or a deep familiarity with language idioms, and they’ll find common ground. It’s like asking about favorite movies; you might get ten different answers, but no one would likely suggest a universally panned film. Clean code doesn’t need to follow one strict pattern; rather, it should avoid the pattern of “don’t be careless,” and seasoned developers can recognize a lack of quality when they see it.
And to continue with the culinary metaphor, some restaurant kitchens are perpetually greasy, with layers of grime built up like neglected cast iron. By contrast, the best 5-star kitchens are whirlwinds of constant cleaning and mise en place. Clean code isn’t useful when it’s treated as mere aesthetic; but materialized as everything-has-a-place, clean-up-as you-go, it becomes a concrete, functional discipline. It’s easy to spot teams that treat their responsibilities like a 5-star kitchen versus those that approach their work with indifference. It may be “just a job” in both cases, but that’s no excuse for negligence.
Even among skilled professionals, there are those for whom even the most glaring issues in quality remain invisible. They aren’t necessarily careless; in fact, they’re often highly responsible individuals, diligent and skilled in other areas of software development, diligent, but without a “hacky” mindset or an innate curiosity for the fine-grained details of coding. They may lack the instinct for technical nuance that reveals poor standards to others. For them, code quality isn’t a primary concern, and issues that stand out starkly to most can go unnoticed, undisturbed by closer scrutiny. Paradoxically, despite their meticulousness and genuine desire to achieve positive outcomes for a project, they tend to underestimate, perhaps, the very set of cogs that will keep the entire machine running smoothly.
Imagine being placed on the stand, compelled to justify your efforts to eliminate unnecessary nested if statements, redundant anonymous scopes, and ineffectual assignments, all while facing the demoralizing question, "What value does removing this add to the project?" Meanwhile, those who create and implement such blatantly incorrect constructs, which are evident even to the most novice developers, do so without fear of criticism or consequence. This stark contrast highlights a troubling reality in many organizations: the relentless pursuit of quick fixes overshadows the commitment to quality, leaving those who advocate for better practices marginalized and disheartened. It is a frustrating paradox in which the path to excellence is questioned while mediocrity continues to thrive unchecked.
Emphasizing core principles like the single responsibility principle, using appropriate data structures, minimal dependencies, and reduced scaffolding can help alleviate recurring friction. Shallow call stacks can also contribute positively, but only when they serve code clarity and maintainability rather than compromise them. When new features or bug fixes continually demand the tight coupling of previously independent components, consider it a red flag indicating structural issues. Forcing decoupled elements into interdependence undermines clean code, structure, and sustainable design, often leading to brittle, difficult-to-maintain systems.
Rushing software to meet deadlines is like planting a time bomb in the project. When the next deadline arrives, all that "quick and dirty" code comes back to slow things down. Ironically, the same people demanding speed rarely think, "Ah, of course, now it all makes sense why it's taking a bit longer." Instead, delays are chalked up to the developers, not to the shortcuts that were pushed through. So, unless it’s a throwaway demo, maintaining a baseline of quality is the only real safeguard, no matter the deadlines set by someone else.
Why do programmers often dread these changes? They usually require discarding a significant amount of existing code. Inertia becomes the antagonist; certainly, that has been my experience when interfacing with large vendors and enterprise developers. If you can overcome this resistance and delete functional but misaligned code to fit the new requirements, you will thrive regardless of the system's design and architecture. But remember, you’ll need solid support to make it happen.
Architecture
As workplace interactions grow more fragmented, code itself has taken on the role of a vital guide for newcomers. With fewer chances for direct mentorship, new developers turn to the codebase as a quiet reference, revealing the decisions, insights, and missteps of those who came before. This dynamic becomes even more crucial in high-turnover environments, where employees often depart in search of a bit of sanity.
But code isn’t the only implicit guide. Architecture, or in large-scale projects, a rigid, top-down vision dictating how each team should operate, also shapes how new members adapt. Instead of each team having the flexibility to develop its own approach, teams are often handed not only principles but also prescribed implementations they must adopt. This standardization, while positive in intent, frequently becomes a burden, turning what might have been a supportive framework into a weight dragging down the project as a whole.
The problem often lies in the fact that these prescribed implementations. Frequently, they are devised by highly smart individuals with a deep understanding of how to build certain types of systems. However, their expertise is sometimes rooted in a specific technology, and when shifting to a new one, they carry over established practices, conventions, and patterns that may not translate well. For a developer accustomed to a more natural and genuinely agile approach, this rigidity can feel forced, as they struggle to understand why things are done in a way that doesn’t fit naturally with the context of the new stack. The result is a something that, while technically functional, stifles adaptability and creates a recipe for misalignment and eventual complications.
Even with documentation organized in a well-structured, searchable format, conveying intent is never foolproof. If this system falls short, it’s unrealistic to expect engineers to sift through guidelines, templates, or frameworks before implementing every new feature to ensure it aligns perfectly. Development isn’t about following recommendations in a one-size-fits-all way; it requires understanding the intent behind them and having the flexibility to adapt them effectively. Over-relying on strict adherence stifles adaptability across teams and ignores the collaborative dynamics essential for large-scale projects.
When ownership fades due to team rotations or unclear roles, the system often becomes inconsistent and harder to maintain. Standards slip, and quick fixes accumulate, leaving behind a messy structure. Assigning ownership to a single developer or small team brings sharper focus and accountability, especially in microservices, where modularity lets each person or team manage their own domain independently. This autonomy encourages responsibility and leads to cleaner, more reliable systems.
Clear ownership also helps reduce technical debt, as dedicated owners are less likely to take shortcuts that compromise future maintainability. In contrast, ambiguous ownership often leads to inconsistent standards and misaligned practices. A microservice-oriented architecture supports this by enabling teams to refine services independently without needing to manage cross-system dependencies. To mention another advantage, as a side effect, monitoring becomes more effective and responsive, as dedicated knowledge allows for quicker, more confident action to reduce downtime and contain issues.
Look, a not-so-unique situation arises in production when an issue repeatedly demands developer's attention, forcing them into a tedious cycle of checking logs and sifting through thousands of lines of data to manually fix the problem. This burden eats up hours that could be better spent on real work. In this context, a monolithic structure, filled with bureaucracy and excessive dependencies, stands in stark contrast to an agile environment. Here, the owner of a microservice can quickly implement a patch after encountering the same issue for the third time, receiving the data needed to easily solve the problem in Slack. Given one or two additional hours, they can fully automate the process, eliminating the need for manual intervention.
However, it’s not uncommon for those used to bureaucratic systems to see agile environments as complicated and overwhelming. The mere mention of a simpler path can shine like an intense light, causing them to shy away in fear. This resistance can block the adoption of more effective workflows, highlighting the need for a cultural shift that promotes flexibility and common sense. Conway might be chuckling in the corner.
In a microservices setup, it’s rare for all teams to converge on a suboptimal approach. With decentralized architectural decisions, different solutions emerge, creating a natural diversity that mitigates risk. Each team operates within small, well-defined contexts, allowing them to iterate and test their ideas. This flexibility contrasts sharply with centralized, top-down structures; when those fail, the impact is widespread, with no room for alternatives. A microservices approach encourages adaptability, giving teams freedom to explore what works best in their specific contexts rather than adhering rigidly to a single system’s constraints.
Rather than imposing a uniform approach across teams, encouraging cross-pollination, where ideas and solutions spread naturally, enables innovation to take root more organically. Each team can adapt its methods to suit specific needs, fostering diversity in approaches that ultimately strengthens the organization far more effectively than rigid, imposed standards.
The notion that every team should 'speak the same language' by adopting cookie-cutter practices and structures is often introduced with the aim of simplifying resource exchanges between teams. However, enforcing strict uniformity, as if to protect developers from minor differences across repositories, insults their intelligence and does them a disservice, offering little to inspire ambitious endeavors.
Alignment
Ultimately, these are just observations. The project provides resources (read: money) so that other resources (read: developers) can implement a certain vision. Whether misguided or not (and sometimes they likely are), those with that vision deserve the chance to see it through. It’s not about proving who’s right or wrong; it’s about aligning on what quality means. They have the right to pursue their path, while developers retain the right to choose an environment where their standards of correctness match those of the company. It’s simply a matter of finding a place where ideas of quality don’t clash.