I am at my company for half a year now and think that I have gotten a fair idea of their codebase. Initially I didn't dare to form strong opinions, but now I start to feel that the code could benefit from a more structure and more software engineering.

My coworkers do a great job of adding new features, creating a cool product and so on. I just feel that there is no second refactoring and cleaning step, just the initial “making it work”. Given that we in the team are all scientists, and it is a growing company with a relatively new and rapidly evolving product, I can see that a flexible prototyping mindset was appropriate. At the university I have seen many projects start and eventually collapse into themselves as the grad student effectively was the project owner, they spend the time during their thesis on “making it work” and cut more and more corners until graduation. The next student would look at their code and usually throw it away. I don't want to see the company plateau in speed, so I believe that continuous refactoring needs to be part of the process.

I've just read “Clean Code” (R.C. Martin), and had years ago read “Code Complete” and “Rapid Development” (S. McConnell). In a small side project I have recently performed refactoring via abstraction. I added dependency inversion on an external library and then exchanged that for a different one. It didn't took long and the result feels amazing. Also I have tried to refactor everything I can and I could directly sense the increase in speed going forward. When I cook, I try to clean as I go to have countertops usable.

Some of my team members have a different perception on this, just like I did years ago. A decade ago I would laugh about Java, how people specified interface IWidget, class Widget and abstract class AbstractWidgetFactory and class FrobnicatingWidgetFactory when they could just have a class FrobnicatedWidget to start with. I thought that having less lines of code would be more readable in every case. But over the time I have changed a bit, I feel that if there are class TextLogger and class BinaryLogger there may is an unwritten structure with an interface Logger wanting to be made explicit. So in our code (which is Python and C++), I see abstractions and patterns which are implicitly present. And I would like to make them explicit.

The co-workers find that adding any encapsulation or standard design patterns to the code explicitly makes it more complicated. Adding another virtual class as the parent supposedly only adds a new class, even though the current classes already have an (implicit) interface. And I want to modularize classes further, they say that increasing the number of classes only increases the complexity. I say that a single class that does too many things is more complicated than the same logic in multiple decoupled classes. But I don't seem to get them to see the things like I do.

I am a scientist programmer myself. I have spent years writing code that works and just left it like that. Only over years of being annoyed with not understanding my own code I came to read about actual software engineering. I still don't dare to fully call myself “software engineer”, but I aspire to get there. And from the books I read I have the impression that I am on the right track. But of course I could be wrong.

I would really just force everyone to read “Clean Code” and to start think exactly like I do and do as I think would be correct. But of course it doesn't work like that. And it doesn't make sense either; my judgement isn't perfect, my experience limited and so on. Rather, I would like to have a professional discussion but would need to have more convincing arguments for the people who feel that refactoring would slow us down and would make the code more complex.

So my questions are:

  • Am I generally on the right track with my perception of conception and mid-term maintenance costs?
  • How can I get buy-in for and cleaner coding from coworkers who have a different perception of software development and mostly focus on getting things working?

8 Answers 8


This is a very common conversation happening in most if not all dev teams. And in the end, it all boils down to this:

The coworkers find that adding these patterns to the code explicitly makes it more complicated.

What is very important to realize here is that both options are complicated, but for entirely different reasons.

To summarize the below explanation: when thinking in the scope of multiple projects (during your career/company's lifetime/...), the effort needed to implement clean coding starts paying dividends. Bad practice coding comparatively trades those dividends away from a quick shortcut, and one that often ends up biting you in the end anyway.

Before I get into that explanation, answers to your direct questions:

Am I generally on the right track with my perception of conception and mid-term maintenance costs?

Yes. Bad practice means taking shortcuts, and shortcuts bite you in the end. For a proof of concept, bad practice is perfectly reasonable, because you have no expectation of long term reliability or maintainability. Once you're entering the maintenance stage, all your bad practice shortcuts are going to catch up with you and bite you back severely.

How can I get buy-in for refactoring and clean coding from coworkers who have a different perception of software development?

I am a big fan of demonstrating issues, because if you can't demonstrate the issues, then you can't prove that there are any issues.

Bad practice manifests in several ways:

  • Projects that never get done and never seem to reach a releaseable stage. Deadlines are forced to shift, and the "just a few more bugs to fix" stage never ends.
  • Bugs that are hard to catch, hard to pin down, and require a long investigation and deep dive into the code
  • Bugfixes often require substantial rewrites
  • Fixing one thing somehow inexplicably breaks something else
  • After investigating a bug, you need to find the specific person who wrote something to ask them about why it was written that way to make sure your change won't break some side effect that you didn't know about
  • You're always worried that any change you make is going to have unforeseen side effects
  • "That's just how it works. Don't ask me"
  • New (or changed) feature requests are met with resistance by the developers because of the amount of effort / rework that it would take
  • There are "here be dragons" parts of the code that no one wants to go into anymore
  • Meetings or discussions on specific issues seem to unavoidably end up needing to bring up some long-winded and ongoing problem/discussion and how the newly reported issue depends on that ongoing one and then the original discussion gets sidetracked by instead continuing the discussion on that ongoing problem.

These are just an aggregation of personal experiences. Probably not all of them apply to you.

Quite often, endemic bad practice (i.e. the workplace culture, not just one bad apple) manifests as problems being publically known/discussed/complained about, it's usually not very hard to come up with concrete examples of how a specific project has been suffering under bad practice decisions.

However, from experience, don't try to be the sole bringer of absolute truth. Because everyone who doesn't already understand clean code is going to list every possible problem they can think of, and if you fail to perfectly answer all of them, that going to be taken as proof that your different way of working is just more troublesome.
Instead, refer to existing materials. Maybe try to suggest that only a handful of people (those who can sway opinion in the team) do some research and watch/read some resources on the topic. Maybe ask if you can apply this in a smaller project.

At a push, I've also refactored parts of the codebase in my own time, just so I could show concrete improvements to a team that kept blindly refuting that clean code is more maintainable. I actually implemented the last three bugfixes in half an hour (total) in front of their eyes, as opposed to the days of meetings/investigation it took them.

It all depends on how open your team is. Maybe they're genuinely interested in improvement and you just have to show them a working example. It's also possible that they're going to outright refuse to innovate their coding "because that's how we've always done it".

This is one of the major struggles that clean coders deal with, and there is no clear cut solution to swaying other's opinions, especially when you're not in a leadership position.

Clean code is indeed quite verbose, and when you're not used to that verbosity, it's hard to wrap your head around. However, once you have experience with it, the verbosity actually helps keep complexity down.

By keeping the code denser, it's easier to see all of the code in a single screen without needing to scroll or navigate between several files, but that starts working against you once you need to actually change that code, because it's so very easy to get distracted or start mistakenly affecting other parts that you did not mean to touch.

Taking a real world example, we could argue the same about a storage facility. Your analogous coworkers would be arguing something along the lines of:

If we stack all the boxes on each other, then it's easier to see at a glance what we have in storage, and we don't need to walk as far.

These are all truths, but they are a biased perception. What this argument glosses over is how much effort it's going to take when you need to get something out of the box at the bottom of the pile, and how much more likely it is to knock a stack of boxes over while you're working in the warehouse.

If we compare the example of a bad practice codebase (dense code, few files), and a good practice codebase (verbose code, many files), and you're a newcomer to both the project and clean coding in general, then you're going to be meeting a learning curve in either case.

In a bad practice project, the learning curve is repetitive. If you go to work on another bad practice project, you then have to learn its pecularities from scratch, and it's always going to be a learning curve.

Similarly, when you hire a new developer for the (first) project, no matter their skill level, they have to learn everything from scratch.

In a good practice project, the learning curve is possibly a bit steeper, but it doesn't repeat. When you move on to another project using similar clean coding, you won't struggle understanding the code structure, because you're already familiar with it.

Similarly, when you hire a new developer for the (first) project, and they're familiar with clean coding guidelines, it will take them significantly less time to get started. They'll of course still have to familiarize themselves with the problem domain itself (which is a given), but not needing to learn the coding architecture is not only a lifted additional burden, a clean architecture can help speed up their ability to browse through the code and understand it.

  • 3
    “Probably not all of them apply to you.” No, they all apply here. But that's good, it is a nice list of arguments to put forward to show that we are in the need to change things. — I have forgotten to mention that my team is rapidly growing. And the established members seem to see less problems with the current state, whereas all the newcomers start to point them out. I have the feeling that this repetitive learning curve is becoming unsustainable with the growing team. Commented Mar 31, 2021 at 12:41
  • 1
    @MartinUeding: The best advice I can give is don't waste the opportunity of newcomers' fresh input, it is a very valuable source of review for your codebase. However, veterans of the team can override that feedback and "turn" the new hire into one of them eventually (depends on the severity of the issues and how stubborn the new hire is), which perpetuates the problem. In the projects that successfully turned around (where I worked), it always happened because management started listening to the newly added consultants over the established dev team. That's not a guarantee, but indicative.
    – Flater
    Commented Apr 6, 2021 at 9:14
  • @MartinUeding: Note that by "newcomers" I mean freshly hired but experienced developers, not entry-level junior developers.
    – Flater
    Commented Apr 6, 2021 at 9:14
  • A few of my colleagues have started around the same time and there seems to be some momentum for this. I hope that we together can make a case to add a bit more structure to the code and its development. Commented Apr 7, 2021 at 7:26

The 2 years of experience me was you, but more extreme. I'd always create interfaces for every class, I'd apply any design pattern where I was able to, I'd never inject any concrete implementation, trying to write code that could adapt to anything ever.

"You're using a database ? Create a repository, and don't forget the interface !!" I'd say.

Now I found things are a lot more complex that I thought. Not every class need an interface. Mostly I like to wait for a good reason to actually do any refactoring. I don't apply design patterns, I just reduce duplication and poof, they "appear".

Am I generally on the right track with my perception of conception and mid-term maintenance costs?

I'm pretty sure you're not. Clean code and those kinds of books are tools to use, not rules of life to follow. Forcing to refactor after every single code written is a mistake. Refactor when you need to, when you feel like it'll actually help your next you to write that next feature about the same concept a lot faster. "Make the change easy, then make the easy change". Well if you coded something which is kind of the same thing twice already, and you know for sure you'll have one or two other occurrences, refactor this to make the next change easy. More code === more knowledge === better decisions.

How can I get buy-in for refactoring and clean coding from coworkers who have a different perception of software development?

Find concrete examples that would make their life easier. That's the point after all. You're not showing your code in a Victoria's Secret private show hoping to seduce some people. The point of clean code is to make your life easier. Also, you're talking to scientists. So prove your point. Use logic. That'll also prove to yourself that some "improvements" are actually useless now, and might be tragic later.

  • 6
    I think that I already went through these docmatic phases. I am at the point where I have concrete examples where the introduction of a pattern would make it easier. I am not advocating to build an AbstractGeneratorDecoratorFactoryVisitor just because I can. I just want to pull out a cleverly indexed array as an encapsulated data structure, I want to split a function into multiple ones. And I feel opposed even on such small changes with a concrete application. Commented Mar 31, 2021 at 9:17
  • 12
    "Now I found things are a lot more complex that I thought. Not every class need an interface. Mostly I like to wait for a good reason to actually do any refactoring. I don't apply design patterns, I just reduce duplication and poof, they "appear"." Indeed they do. You realize that "it's complicated" way after you reached the point of "I think I know everything now". Don't always use pattern X. Write good code and pattern X (or any other pattern) will emerge on its own, if appropriate. That's what "a pattern" is, after all! :)
    – AyCe
    Commented Mar 31, 2021 at 18:38
  • 10
    Mostly I like to wait for a good reason to actually do any refactoring. I don't apply design patterns, I just reduce duplication and poof, they "appear". Yep. Whenever I hear someone pushing "patterns", I think two thing: 1) Why are you trying to force-fit something into something restrictive like a "pattern"? and 2) When you get some more experience writing code, are you going to take the (*!&@(*& training wheels off your bicycle? Commented Mar 31, 2021 at 19:32
  • 5
    Slightly OT, but, the reason "give every class an interface" is recommended in many places is because, in some languages (C#), that's the only way to mock a class. Thus "injecting a concrete instance" is usually not an option if you want to unit test the class in isolation. In other words, the recommendation is really a workaround to a language flaw, not a "true" best practice. Commented Mar 31, 2021 at 23:57
  • 4
    You're not showing your code in a Victoria's Secret private show hoping to seduce some people. Citation needed. Commented Apr 1, 2021 at 17:55

TL;DR - You need Unit Testing

Unit Testing is a phrase which I haven't seen mentioned anywhere else in this thread so far.

(At least, from reading the original post I'd have been willing to bet that you aren't writing unit tests because it sounds like your codebase is virtually untestable.)

As others have pointed out, Uncle Bob's advice, along with SOLID principles and the use of all sorts of other clean code techniques can often seem to conflict with other important principles such as YAGNI, KISS and The 'Nike' Principle (JFDI)

Loose coupling can feel good as a developer, but by itself does not actually achieve anything tangible that you can point out to your boss/co-workers/stakeholders; it only has potential hypothetical value in the future in case something may change.

Future-programming is a trap - you invest an awful lot of time and effort into something that you will probably never need and the time is wasted. Great, you have a bunch of nicely structured classes which only depend upon interfaces or other high-level abstractions, but if nothing else in your entire solution ever implements those interfaces and those small classes are never reused, then

  1. None of your stakeholders (customers, product owners, testers, senior management) could give a fig
  2. Now your code is more complicated than it probably needed to be.

Okay, so what does that have to do with Unit Testing..?

As it turns out, aspects of SOLID along with other software engineering guidelines about separation of concerns, modularity, pure functions, etc, are all useful for unit testing.

The goal of needing to write unit tests means that you suddenly have a compelling need for smaller, simpler classes/functions and loosely-coupled code, otherwise you're in for a really hard time as soon as you sit down to figure out how on earth you're going to test something properly.

Unit testing means that any function, class or interface you write is always used in at least two places; the concrete implementation in the real code, then with the tests and mock/stubs to replace dependencies.

Classes are really hard to test when:

  • It's tightly-coupled to loads of other classes/functions so isolating "just" the code you want to test is extremely cumbersome
  • It depends upon state outside of your control (e.g. DateTime.Now, Random.Next, etc)
  • The amount of setup and mocking required to satisfy a class under-test creates a lot of bloat in the test code
  • Functions/methods within the class have far too many paths and are doing too many things for someone to easily reason about, requiring a very long list of test cases to achieve full coverage.

SOLID principles are not a guarantee that your code is easily testable, but easily-testable classes generally look remarkably like code which follows SOLID principles (Or, to put it another way, if you write your code in a way which is easy to test, you'll probably take a step back and say "Hey, this looks a lot like the 'clean code' Uncle bob was banging on about")

Easily testable classes often have the following properties:

  • As few responsibilities as possible (Ideally one - SRP), and functions which only do one thing.
  • Classes and methods which are unlikely to need modifying in future to avoid knock-on-effects to existing tests. (OCP)
  • Dependencies which are easily able to be replaced with mocks/stubs (LSP)
  • Dependencies which only expose functionality required by a class and easier to mock/replace (ISP)
  • Classes with as few direct, concrete dependencies as possible (Ideally none - DIP)

With unit tests in the picture, smaller, loosely-coupled classes and interfaces are no longer something which might be useful in some unlikely, hypothetical future scenario. They're something which you will actively require to be able to write tests, and will need those tests to pass before your code is delivered into production.

OK, Great, But we don't do unit testing!

Instead of trying to convince anyone around you of the virtues of small reusable classes and loosely-coupled code, and why they should have to put up with the extra complexity, sell them the virtues of unit testing. The fact that it changes the way you write your code is really a side-effect; the real benefits of unit testing are in other areas such as:

  • Protection against future regression issues. Anyone changing or refactoring the code in future will be alerted if they've broken something which used to work
  • New developers unfamiliar with the code can read the tests to understand how it's supposed to be used and what it's supposed to do.
  • Prevent repeatedly re-introducing the same bug by creating a test for a case when someone finds a problem.
  • Allowing the process of writing a test to help shape the way you structure the code by choosing whatever option makes tests easier to write.
  • Reduce time spent manually re-running the same regression tests over and over.
  • 1
    Downvoter - please could you comment and feedback on your reason for downvoting, since that helps improve the quality of the site for everyone. Commented Mar 31, 2021 at 17:04
  • 2
    I find DI/IoC to be a complete pain in the ass, especially when using some framework to make it "easier". So there needs to be a good reason to use it. For me, pretty much the only good reason to use it is that it makes unit testing possible. So I think @BenCottrell has hit on one of the main "selling points" for this extra complexity for the team
    – Greg Woods
    Commented Mar 31, 2021 at 22:44
  • 7
    Amazing answer, I often struggle to explain to people that testing is not just about testing, it is how it affects your design decisions. Commented Apr 1, 2021 at 1:04
  • 7
    @Frax: In my experience, you just wait until someone has (1) thoroughly tested something, (2) found a problem that needed fixing, and then (3) manually had to do all those tests again. You listen to them complain about it, and then you casually mention the idea of automated testing. The idea that, after you changed something, you only click a button and all those tiresome tests are done automatically. This gets them started with integration tests (which really get the most bang for your buck), and once they are hooked, the path to unit tests follows naturally...
    – Heinzi
    Commented Apr 1, 2021 at 21:17
  • 2
    The real advantage to me is when doing Test Driven Design because the resulting aPI's simply get better. Commented Apr 2, 2021 at 8:56

My coworkers do a great job of adding new features, creating a cool product and so on. I just feel that there is no second refactoring and cleaning step, just the initial “making it work”.

After a while on this site, I came to the conclusion that I was not alone. This is something that transcends our teams.

But don't get me wrong, I feel you. I'm a passionate and enthusiastic software engineer that once thought he was good at doing his job but later when I joined this site I discovered how wrong I was. But most important, how closed-minded I was regarding what was "right", "good", "best", etc. Like you, I have had strong beliefs in subjects like Clean Code. Such strong beliefs and closed-mindset were causing me a sort of resistance to believe that "perfect" is the enemy of "good". Between "perfect" and "good" there's a place where things are in balance and keep working well.

Try to put yourself in your coworker's shoes. You will find that they are somewhat right when they say that some changes could be good but the tradeoffs don't compensate. Complexity is the common one. Abstractions are among the harder things to do well in this job. It's stupidly easy to make useless abstractions, lead by false premises or assumptions of changes in the future that never happens as expected. There could be other reasons no so obvious too.*

Am I generally on the right track with my perception of conception and mid-term maintenance costs?

Objectively, it's likely any refactor addressed to ease everybody's life is going to result in better ROI eventually. The thing is. Is your team or customer suffering from ROI issues? Are you all having troubles with new developments, bug fixing, meeting deadlines, or any other issues caused by an uncontrolled technical debt?

If not, let it go. For now. Changing the mind about this will take you to change too many minds and buy too many wills. Not only developers but also PM and stakeholders too. And trust me, if you can't convince your coworkers, you are nowhere close to make it reasonable to laymen.

By convincing I do mean to make'em feel that changes are necessary. The only force that makes us change is pain. Nobody changes their mind without a good reason, and suffering is the stronger one.

How can I get buy-in for refactoring and clean coding from coworkers who have a different perception of software development?

Don't sell it. Give it for free. Start with affordable and limited impact changes. Let these changes speak for you. If these are proven to make everybody's life easier and the project profitable, then everybody will buy it. Funny thing is that many won't even notice they did. But be aware of this, you are absolutely accountable for the changes. For the better or for the worse. It's a personal bet that nobody else wants to take responsibility for. Pick your fights wisely.

For larger refactors, remember that some changes can not be taken immediately. Refactoring should not result in rewriting half the project in 2 weeks sprint. That's indeed too risky. The state of the art is not an end, it's rather a road, which shifts now and then as the needs change.

I am a scientist programmer myself. I have spent years writing code that works and just left it like that. Only over years of being annoyed with not understanding my own code

This is all about perception. Yours will change as you grow as a professional and learn new things. The best way to never stop learning is to keep oneself open-minded and willing to question an relax their belifs.

What are they all afraid of?*

Often than not, the fear is on the changes and the uncertainty that these changes imply.

When I find resistance to pay off technical debt, I look immediately at the tests, because (IMO) they are implicitly saying to me that they don't trust in their tests.

If I need to encourage coworkers to feel comfortable refactoring code, I show'em how to do so with tests. For example, I turn legacy code (code without tests) into testable code. Once we all agree with the results, I do refactor the production code under tests until all tests go green. Teaching how to make tests worked really well in my case because others learn how to play safe.

As they get more confident at doing this, they will feel more confident with larger refactors. As they do, it's likely some will ask you how to do so. At this point, they are curious about best/good practices. To me, this is a good moment to share our references (books, articles, tutorials, courses, etc).

  • “The thing is. Is your team or customer suffering from ROI issues?” Yes! I've spend the last five months just squashing bug after bug and there are still more to go … — “And trust me, if you can't convince your mates …” I have a bunch of coworkers on my side. It is just that all of them are rather new. The established team seems to have less problems with the existing code. I start to see the code complexity as an onboarding hurdle, and that is why established people don't see the problem as strongly. Commented Mar 31, 2021 at 12:47
  • Note that i have spent the last five months just squashing bug after bug and there are still more to go doesn't seem to be an issue for the one paying the bills. As commented before, your job is not saving the company from its mediocrity. Try to don't own the code or make yourself responsible for the overall state of the project. You won't inherit the company. Do your best and nobody will ask you for more. That's it
    – Laiv
    Commented Mar 31, 2021 at 12:57
  • Doing my best involves pointing out problems in the process when I see them. And I tend to identify myself with my work projects, so you're right, I should be careful to keep a work-life-separation there. Commented Mar 31, 2021 at 15:11
  • 2
    Doing my best involves pointing out problems in the process when I see them exactly. Let others know and let accountables decide.
    – Laiv
    Commented Mar 31, 2021 at 15:13

Sell the benefits to the business, not the programming approach.

  • Do users find bugs? Does this impact the business? Clean code reduces bugs.
  • Is making changes hard? Is is slow and cumbersome? Clean code reduces this burden.
  • Do tests fail a lot intermittently? Quality tests reduces that.
  • Do tests take a huge amount of time to run? Good application of the agile testing pyramid can help avoid that
  • Is it hard and/or slow to on-board new engineers? Clean code with good abstractions can help with that.


To have these as recognized business benefits it can help to have an engineer run the company. This is what has led to many silicon valley successes. An engineering first approach to quality leading to business success. Otherwise the notion that clean code reduces 'x' is going to be a constant and never ending argument.

  • You've assumed that the claim "Quality code reduces <x>" is going to be accepted automatically. And also that "Quality code == built in the style of 'Clean Code'". If those premises were accepted already the question wouldn't need asking.
    – Brondahl
    Commented Apr 3, 2021 at 9:00

First of all, it is important to understand that software is worth only as much as it captures quality knowledge about the problem domain it is being built to be solution to.

No amount of patterns or clean code can be a substitute for that. I guess, your codebase is growing and your understanding of the problem and solution grows as well. Is it well-captured?

  1. Am I generally on the right track with my perception of conception and mid-term maintenance costs?

Sure, you are right. If most of the team learns how to write clean code (not necessarily the Clean Code), but you can agree on some quality standard and try to keep it.

Unfortunately, your colleagues should see the benefit. Writing tidy code takes time to learn, but also deeper understanding of the domain. And you may need to be ready for sacrifices. For example, some models may require a big pile of parameters. There is no way around: maybe, that is how that function is presented in some Fortran cookbook from 1960s and there is hardly any need to make it cleaner because it's proven to work.

Here the advice is: don't force impossible. Take small steps. First take steps, which bring most of the value. Take code reviews into use. Start writing tests for some critical areas or maybe even formal proofs for the most critical parts. If you are very good at clean, nice, simple code, you can gradually rise the whole team to the new level. Demonstrate, that even small step towards better code brings fruits. It may be necessary to set for a lesser standard at first: It is also possible to write quite good code without the latest SuperPuperFactories.

  1. How can I get buy-in for refactoring and clean coding from coworkers who have a different perception of software development?

There is no way but show how tidier coding practices will pay off. From your description it seems that once the scientific problems are solved the whole system will need to be rewritten from scratch by "industrial programming" guys (as opposed to "research programming" ones). So it may already be too late to apply clean code in your situation, just prepare yourself, the team and management that "this mess" is quicker to write from scratch than refactor.

Here I return again to the software is knowledge I started this answer with. Patterns, SOLID, DRY, etc only help to organize knowledge (they are like shelves for the books). In case of rewrite what matters is the domain knowledge, which is the only thing worth to transfer to the system 2.0. So perhaps to be professionally honest you can concentrate on code-organizing efforts in that particular area. Do not spend time on auxiliary or generic subdomains (in DDD - domain driven design terms) - apart from security concerns (which require everyone's attention), those can be fixed by "industrial" guys or even with commodity off-the-shelves software.


Some amazing well thought out answers here. As has been stated, start with small suggestions.

You really will need to work on your soft skills to be persuasive. Coming in all guns blazing as a newly converted "Uncle Bob Disciple" isn't going to make any friends!

Consider making notes of bugs that have found their way onto live systems, note the time taken to fix them, what work was delayed as a result. Gather evidence!

Whilst some developers love to be the "Knight in shining armour", riding in to save the day, I would hope that the majority would prefer a more stable life, and prefer not to be woken in the night because something broke. If you can accumulate evidence, it may help the cause. You may even be able to get a manager to back your efforts if you can show longer term benefits. Your goals should focus on stability, predictability, longer term viability of the development team, rather than the technique or pattern you want to introduce. Ideally other team members should be suggesting those patterns after they are nudged in the right direction by your data and reasoning.

Code reviews have been mentioned already. One idea is to introduce code reviews with a single simple criteria... "Could a developer who started 3 months ago understand this?". I've worked at places where 12 months experience wasn't enough to figure out some of the code changes! This step should start conversations about smaller methods, more verbose code, and maybe even unit testing to express the intent of the feature.

One other idea... Retrospectives... as a forum for discussing improvements to the process. But there could be a problem if it turned into your personal rants about the team, processes and code!

Finally, best of luck. enjoy the challenge, and don't get too frustrated.


You say…

I see abstractions and patterns which are implicitly present. And I would like to make them explicit.

Yeah, I remember having that experience. Now, listen, you are better of reaching an understanding with your coworkers. And that is likely to be a middle ground.

They say that increasing the number of classes only increases the complexity. I say that a single class that does too many things is more complicated than the same logic in multiple decoupled classes.

You are both right, kind of. You are not necessarily making the code more or less complex one way or the other. You might just be moving complexity around (read "Law of conservation of complexity").

I approve of leaving code better than you found it. But better, if not in terms of complexity, according to what? Well, code can be equally complex, but nevertheless easier to understand, easier to read, easier to reason about. And I want to reiterate, you need to reach an understanding with your coworkers, and that includes the code. If you refactor the code to something they don't understand or find hard to work with, it would be detrimental.

On that note, you can consider your coworkers as users of the software. When you introduce an interface for other developers to use, they are your users. Then it make sense to design taking them into account.

By the way, not all refactoring is at large scale. There is value in going into those complex classes and apply scoped refactoring to separate concerns (and I remind that separation of concerns is not the same as single responsibility principle). If you can make the code more readable (and that might mean extracting a class, which could be reused, for example), I assure you that the team would find that more valuable than, say, IWidged.

Am I generally on the right track with my perception of conception and mid-term maintenance costs?

With the caveat that we only have your account to judge. I agree that refactoring is necessary. And I agree that stopping at "making it work" is a bad practice. They are likely creating a lot of technical debt.

Yet, you should not try to anticipate requirements. When your predictions fail, you will find that your code flexible where you don't need it to be, and rigid where you need to be flexible.

Instead refactor and apply patterns to ease implementing new requirements. Once new requirements arrive, you know what kind of changes you need. Refactor accordingly.

How can I get buy-in for refactoring and clean coding from coworkers who have a different perception of software development?

This is not something I would try to force. However, you could lead by example. If you are actually doing it right, they should be able to see it. And for what you say, that is not what's happening.

There are of course, some adjacent practices that should be there. That I hope are there. These includes version control, automated testing, and a backlog or issue tracker (or equivalent) to say the least. If these aren't there, please advocate for them.

And I remind you that there is always value in further learning. Both for you, and for them.

Among other things to put your time on, consider writing tests. They should build a safe bed for refactoring, plus they may lead to discovering some defects, and fixing them could be a good reason for some refactoring. On the chance you don't find that activity engaging enough, pick some improvement that have proven difficult to implement. Just not too difficult that it is frustrating. And again, implementing that could be a good reason for some refactoring.

Anyway, I remind you to pick your battles.

Addendum: I wrote the answer before reading in comments that the team is growing, that the complexity is a problem for on-boarding, and that there are some people on your side. Now, if the people against are simply on the camp of "if it ain't broke, don't fix it"... Then provide tests and refactor. Once the change is done and work it, they are more likely to accept it. However, you be ready to answer questions. As long as it is not over-engineered, you should be able to explain it and justify it. Not only to any detractors, but to new comers.

  • "separation of concerns is not the same as single responsibility principle" What's the difference? Commented Apr 1, 2021 at 20:21
  • @reggaeguitar For a piece of code, everything you need to consider is a concern. A single routine will have multiple concerns. And a concern can affect multiple routines. Separating them means that you should write the code in a way that you can consider one without worrying about the others. However, by single responsibility principle, a module should take care of a single responsibility. I'm taking responsibility to mean reason for change, and external to the system. And yes, that would mean that responsibilities are concerns too.
    – Theraot
    Commented Apr 1, 2021 at 20:54
  • @reggaeguitar We often think of separation of concerns at a smaller scale. It happens inside a method, for example. It is in the way you write them. It does not have to mean extracting routines or classes. However, it can be useful to extract methods and classes for separation of concerns. In particular generators are a very useful tool for that. Also you may introduce new classes to make invariants concrete, or to reuse. Is that discovering or creating responsibilities? I'm not sure how to tell. We can certainly invent responsibilities. At the end of the day, these are guidelines.
    – Theraot
    Commented Apr 1, 2021 at 21:08
  • 1
    @reggaeguitar Perhaps example work better than my word soup. async/await allows you to worry about what the code does without worrying about concurrency. Linq allows to separate where you get your data, from when to stop, from what you do with it. Without it you will have nested loops, that interleave those concerns. RAII allows you to separate deallocation. And you could work around a language that does not support such things, but that would require to introduce other code to make it work. Often some patterns, which require new classes. Plus the discipline to use them.
    – Theraot
    Commented Apr 1, 2021 at 21:29
  • The examples made it clear what you meant, thanks for your response! Commented Apr 1, 2021 at 21:35

Not the answer you're looking for? Browse other questions tagged or ask your own question.