Your Linting Rules: Are They the Police or a Teacher?

I. Introduction

In the fast-paced world of software engineering, the quest for code quality is never-ending. As organisations scale and codebases grow, maintaining consistency and preventing bugs becomes increasingly challenging. Enter linting: the seemingly perfect solution to all our code quality woes.

It’s a familiar scene in engineering teams across the globe. A passionate developer, let’s call them our “hero engineer,” identifies the root of all evil: inconsistent, potentially buggy code repeated throughout the codebase. Their solution? Implement a series of good practice linting rules to revolutionize the way people code. With the best of intentions, they charge forth, determined to elevate the entire team’s coding standards.

The promise is enticing: with these new linting rules in place, surely the code will magically improve. After all, if engineers can’t merge without passing these checks, they’ll have to write better code, right?

Wrong.

II. The Problem: When Linting Becomes Policing

In the complex ecosystem of a modern engineering organization, introducing new rules without proper context can lead to unexpected – and often counterproductive – results.

A. Engineers’ Reaction to Unexplained Linting Errors

Picture this: An engineer, deep in the flow of solving a critical problem, suddenly encounters a barrage of linting errors in their IDE or CI pipeline. These errors, appearing out of nowhere, seem to have no relation to the functionality they’re implementing. What’s their instinctive reaction?

More often than not, the goal shifts from “write good code” to “make the errors go away.” This usually involves finding the quickest path to silence these annoying new alerts that have suddenly appeared in their workflow.

Let me share a real-world example I’ve encountered:

In a production repository I once worked on, I witnessed this scenario unfold. Our well-intentioned “hero engineer” had implemented strict linting rules overnight. The next day, pull requests were failing left and right due to linting errors. What happened next was eye-opening.

Instead of embracing these new rules, engineers started adding // eslint-disable-next-line comments liberally throughout the codebase. Others went a step further, adding /* eslint-disable */ at the top of entire files. The very tools meant to improve code quality were being systematically circumvented.

This behaviour isn’t born out of malice or laziness. It’s a natural response to a perceived obstacle in the development process, especially when the benefits of these new rules aren’t clear or immediate.

And this inst a totally fictional tale, below example from production code bases I’ve witnessed people similar to our hero try this on

B. The Temptation to Force Rules

Faced with this resistance, our hero engineer might be tempted to double down. “If people won’t follow the rules voluntarily,” they think, “we’ll have to force them.” This usually involves:

  1. Putting codeowners on lint configuration files
  2. Implementing additional scripts to check for inline linting disables
  3. Blocking merges for any code that doesn’t pass linting

“If all you have is a hammer, everything looks like a nail.” — Abraham Maslow

Suddenly, our well-meaning engineer finds themselves in an ongoing battle with their own colleagues. The very team they sought to help now views them as an adversary, the enforcer of arbitrary and frustrating rules.

C. The “Military vs. Police” Analogy

This situation reminds me of a quote from the 2004 series Battlestar Galactica. Admiral Adama says:

“There’s a reason you separate military and the police. One fights the enemies of the state, the other serves and protects the people. When the military becomes both, then the enemies of the state inevitably become the people.”

While we’re not dealing with matters of state security, the principle holds true in software engineering. When we turn our tools of improvement into our “military might” against our own team members, we risk turning them into adversaries rather than collaborators.

In a modern engineering organization, where collaboration and shared ownership of code quality are crucial, this adversarial approach can be toxic. It creates an “us vs. them” mentality, where developers feel policed rather than supported in their efforts to improve.

The result? A team that’s more focused on appeasing the linter than on writing genuinely good, maintainable code. The very tool intended to improve code quality becomes a bureaucratic hurdle to be overcome, rather than a valuable aid in the development process.

So, if forcing linting rules upon the team isn’t the answer, what is? How can we harness the power of linting tools without creating a police state in our codebase?

III. The Alternative: Linting as a Teaching Tool

“You never change things by fighting the existing reality. To change something, build a new model that makes the existing model obsolete.” – Buckminster Fuller

So, if enforcing linting rules like a code police force isn’t effective, what’s the alternative? The answer lies in a fundamental shift of perspective: from policing to teaching.

It’s about the Importance of Education and Buy-in. In modern engineering organizations, where autonomy and expertise are valued, dictating rules without explanation is rarely effective. Instead, we need to focus on education and securing buy-in from the entire team.

Remember: if your team doesn’t believe in the linting rules, they won’t follow them — at least not in the spirit they were intended.

Starting a Dialogue with Your Team, the key to successful implementation of linting rules is open communication. Here’s how to approach it:

  1. Agree on the reasons for linting: Is it for bug prevention (quality) or increasing readability (velocity)? Make sure everyone understands and agrees with the goals.
  2. Collaborative rule-setting: Involve the team in deciding which rules to implement. This isn’t just about democracy — it’s about leveraging the collective expertise of your engineers.
  3. Use tools like Code Coach: For teams working with external contributors or in code review scenarios, tools like Code Coach can help enforce agreed-upon standards without feeling heavy-handed.

Implementing Linting Effectively, Once you have buy-in, consider these strategies for smooth implementation:

  1. Plan for legacy code: Create a plan to clean up existing code gradually. Automation can be your friend here — look for existing code fixes or write your own if needed.
  2. Fail fast: Implement linting warnings as close to the development process as possible. IDE warnings are far less frustrating than CI pipeline failures.
  3. Document and explain: Ensure that every linting rule has a clear explanation and, if possible, a link to further documentation.

IV. Best Practices for Educational Linting

Now that we’ve shifted our mindset from policing to teaching, let’s explore some best practices that embody this educational approach.

Try to Explain the ‘Why’ Behind Rules. Unhelpful errors like “don’t do this” teach obedience, not understanding. We’re dealing with knowledge workers, not factory line operators. Put the ‘why’ in your errors, and if your linter supports it, link out to documentation.

For example, in C#, using Roslyn metadata can embed links to documentation directly in IDE error messages, providing immediate context and explanation.

BYO (Build Your Own) Rules When Necessary. Most of the time, the best way to start is by agreeing on standards within your team. Document these in an easily accessible format — markdown files in your repo can work well.

From this document, look for existing rules in the market that match your standards. If you can’t find any, don’t be afraid to write your own. Most language-specific rules are fairly trivial to write, usually requiring only 10-20 lines of code each.

You can read in this post about how we approached this is Agoda many years ago.

Please, please Use Auto-formatters, if you’ve established style standards, don’t make people implement them manually. Every major programming language has formatters that work with IDEs. Make use of them!

Here’s a story that illustrates the importance of auto-formatters:

I once had an engineer who was the first external contributor to a particular Scala backend in our company. He sent a PR with about 500 lines of code changed or added. The review came back with 140 comments — about one comment for every three lines. It was escalated to the Director/VP level because it seemed so egregious.

When we dug into it, we realized about 80% of the comments were purely about style: “You need a line feed before this brace,” “This brace needs one more tab in front of it,” and so on.

After this realization, we de-escalated the situation.

But here’s where the story takes a positive turn: my engineers did a follow-up PR to add Scala style configurations to the repo. They went through all 140 comments and reverse-engineered a Scala style config that suited the team’s preferences. They even held a knowledge-sharing session afterward.

That right there is good culture. Instead of assuming the contributor was careless or incompetent, the team recognized a knowledge gap around the tooling and filled it, then shared that knowledge.

Automate Fixing Where Possible. Most linters are based on AST (Abstract Syntax Tree) queries, which means you can often apply mutations to the AST to automatically fix issues. This makes it even easier for developers to comply with standards.

Here’s another story that illustrates this principle:

Whenever a new version of C# would come out, Microsoft would often include code fixes in Visual Studio to convert old language patterns to new ones. This became my personal way of learning new language features. My IDE would suggest, “This is a code smell. Let’s fix it,” and then I’d apply the auto-formatting to see a new, often more concise or readable way of doing things.

By automating fixes interactively via teh IDE while engineers are coding, you’re not just enforcing standards — you’re actively teaching developers new and improved coding patterns.

Remember, the goal isn’t to force developers into rigid compliance. It’s to create an environment where writing high-quality, consistent code is the path of least resistance. By focusing on education, collaboration, and automation, you can transform linting from a policing tool into a teacher that scales, elevating the skills of your entire engineering organization.

V. Creating a Culture of Continuous Improvement

In modern engineering organizations, the way we approach code quality can significantly impact team dynamics, productivity, and overall job satisfaction.

Don’t Assume Malice or Incompetence, when faced with code that doesn’t meet our standards, it’s easy to jump to conclusions about the developer’s skills or intentions. However, this mindset is rarely productive and often inaccurate.

Remember: In almost all cases, developers aren’t writing “bad” code out of laziness or incompetence. They usually haven’t been shown a better way yet. This principle applies not just to linting, but to all aspects of tooling and best practices.

Foster an Environment of Knowledge Sharing, try to create a culture of continuous improvement means making knowledge sharing a core value of your team. Here are some ways to encourage this:

  1. Regular code review workshops: These can be opportunities to discuss common issues found in reviews and share solutions.
  2. Linting rule of the week: Highlight a specific linting rule each week, explaining its purpose and demonstrating good practices.
  3. Pair programming sessions: Encourage developers to work together, especially when implementing new patterns or working with unfamiliar parts of the codebase.
  4. Tech talks or brown bag sessions: Give team members a platform to share their knowledge about tools, techniques, or interesting problems they’ve solved.

Encourage Feedback and Iteration on Linting Rules, remember, your linting rules shouldn’t be set in stone. As your team grows and your codebase evolves, your needs may change. Create a process for regularly reviewing and updating your linting rules. This might include:

  1. Quarterly linting reviews: Discuss which rules have been helpful, which have been pain points, and what new rules might be beneficial.
  2. An easy process for proposing changes: Make it simple for any team member to suggest modifications to the linting rules.
  3. Trial periods for new rules: When introducing a new rule, consider having a “warning only” period before enforcing it, allowing the team to adjust and provide feedback.

VII. Conclusion: Embracing Linting as a Teaching Tool

The Teacher Approach vs. The Police Approach, we started by examining the common pitfall of treating linting rules as a policing tool. We saw how this approach often leads to resistance, workarounds, and a adversarial relationship between developers and the very tools meant to help them.

In contrast, we’ve explored the benefits of treating linting as a teaching tool. This approach focuses on education, collaboration, and continuous improvement. By explaining the ‘why’ behind rules, involving the team in rule-setting, and fostering a culture of knowledge sharing, we can transform linting from a source of frustration into a catalyst for growth.

B. Long-term Benefits of Educational Linting

The benefits of this educational approach extend far beyond just cleaner code:

  1. Improved Developer Skills: By understanding the reasoning behind linting rules, developers become more skilled and conscientious coders.
  2. Increased Team Cohesion: Collaborative rule-setting and knowledge sharing foster a sense of shared ownership and team unity.
  3. Faster Onboarding: Clear, well-explained coding standards make it easier for new team members to get up to speed quickly.
  4. Adaptability: Regular review and iteration of linting rules ensure that your practices evolve with your team and technology.
  5. Positive Engineering Culture: An approach based on teaching and collaboration contributes to a more positive, growth-oriented engineering culture.

C. Call to Action: Evaluate and Improve Your Linting Culture

As we conclude, I encourage you to take a step back and evaluate your team’s current approach to linting:

  • Are your linting rules serving as a teacher or a police officer?
  • Do your developers see linting as a helpful tool or a frustrating obstacle?
  • Is there open dialogue about coding standards and best practices?

If you find that your current approach leans more towards policing than teaching, consider implementing some of the strategies we’ve discussed. Start small – perhaps by initiating a team discussion about one or two linting rules. Remember, the goal is not perfection, but continuous improvement and learning.

By shifting towards an educational approach to linting, you’re not just improving your code – you’re investing in your team’s growth and creating a more positive, collaborative engineering culture.

VIII. Additional Resources

To help you on your journey towards more effective, educational linting, here are some additional resources you might find useful:

Further Reading on Effective Code Review and Team Collaboration

  1. “Best Kept Secrets of Peer Code Review” by Jason Cohen
    • A comprehensive guide to effective code review practices.
  2. “The Art of Readable Code” by Dustin Boswell and Trevor Foucher
    • Offers insights into writing clear, maintainable code.
  3. “Clean Code: A Handbook of Agile Software Craftsmanship” by Robert C. Martin
    • A classic book on writing quality code that’s easy to understand and maintain.
  4. “The Pragmatic Programmer: Your Journey to Mastery” by Andrew Hunt and David Thomas
    • Provides practical advice for improving as a programmer, including tips on code quality and team collaboration.

Remember, the journey to better code quality is ongoing. Stay curious, keep learning, and always be open to new ideas and approaches. Happy coding!

Leave a comment