Your Clippy Config Should Be Stricter
“If it compiles, it works.” This feeling is one of the main things Rust engineers love most about Rust, and a reason why using it with coding agents is especially nice. After debugging some code that compiled but mysteriously stopped in production, I realized that it’s useful to enable more Clippy lints to catch bugs that the compiler won't prevent by itself. It's especially useful as guardrails for coding agents, but stricter linting can make your code safer, whether or not you’re coding with LLMs. Scour is the personalized content feed that I work on. Every Friday, Scour sends an email digest to each user with the top posts that matched their interests. On a recent Friday, the email sending job mysteriously stopped. This was puzzling because I had already put in place multiple type system-level safeguards and tests to ensure that it would continue with a log on all types of errors. After digging into the logs, I found the culprit to be . A function naively truncated article summaries without checking for UTF-8 character boundaries, which caused a panic and stopped the Tokio worker thread running the email sending loop. The solution for this particular bug was a safer method for truncating article summaries that respects UTF-8 character boundaries. However, this problem was reminiscent enough of the 2025 Cloudflare bug that "broke the internet" that I wanted some more general solution. Rust's compiler prevents many types of bugs but there are still production problems it can't catch. Panics will either crash your program or quietly kill Tokio worker threads. Deadlocks and dropped futures can make work silently stop. And plenty of numeric operations can silently cause incorrect behavior. We can stave off many of these types of bugs by making Clippy even stricter than it already is. This is especially relevant in the age of coding agents. A seasoned Rust engineer might naturally avoid patterns that could cause problems. An agent or a junior colleague might not. Stricter Clippy rules make it easier to rely on code you didn't personally write. Also, enabling new lints on an existing codebase is tedious, and exactly the kind of task that is good to hand to a coding agent. Clippy ships with hundreds of lints that are disabled by default. Some are disabled because they might have false positives and some are style choices which you might reasonably not want. Which lints should we enable to help us get back the "if it compiles [and passes Clippy], it works" feeling? Clippy's lints are grouped into categories : Correctness, Suspicious, Complexity, Perf, Style, Pedantic, Restriction, Cargo, Nursery, and Deprecated. Unfortunately, none of these categories cleanly map onto "don't let this panic or do the wrong thing in production". In fact, the Clippy docs say that "The category should, emphatically , not be enabled as a whole." Clippy even includes a dedicated lint, , to discourage you from enabling this category. While the category includes many useful lints, it also includes some that directly contradict one another. For example, it contains lints to enforce both and . The docs say "Lints should be considered on a case-by-case basis before enabling". Of course, you can enable whole categories like and and then specific ones you want to disable, but I'm outlining a selective opt-in here. Even if you don't use a certain pattern in your code base today, it's not bad to enable the lint anyway. Inapplicable lints serve as cheap tripwires in case the given pattern is ever added later, whether by you, a colleague, or a coding agent. Every project is different and you should look through the available lints to see which ones make sense for your project. Also, check when lints landed in stable if your Minimum Supported Rust Version predates 1.95, as some of these may have been added after your MSRV. With those caveats out of the way, here are the lints I enabled, roughly categorized by what kind of behavior they prevent. You can skip to the bottom if you just want to copy my config . This group prevents panics from unwraps and unsafe slicing or indexing into arrays and strings. Note that some of these, like and may produce many warnings throughout your code base. That may be annoying to fix. However, using safe methods like and iterators instead of slicing prevents pretty severe footguns, so I would argue that it's worth it. You might or might not want to enable . Calling on an or can result in a panic. However, the message you pass to should already document why that thing shouldn't happen. Enabling the lint and then selectively disabling it throughout your code with may end up duplicating the same rationale for using it in the first place. Another lint that is a real judgement call is . This can prevent overflows and division by zero. However, it will cause Clippy to warn you about every place you use math operators: , , , , , and . I tried enabling it in my code base and would estimate that around 15% of the warnings caught real issues and 85% was just noise. These prevent various concurrency bugs and deadlocks: The lints , , effectively force you to document invariants when doing lossy casts between numeric types. You might or might not find that useful. These two are especially useful if you're using a coding agent. Instead of letting the agent write , it should provide a reason wherever it's disabling a lint. If you're using a Cargo workspace, you'll want to enable these lints in the workspace Cargo.toml. Unfortunately, each workspace crate needs to opt in to inheriting lints with , rather than inheriting the lints by default. On nightly, there's a lint that specifically checks for this. If you're using stable Rust, you can use or a simple shell script run on CI to make sure you don't forget to make a workspace crate inherit the lints. When enabling lints, you can either set Clippy to or them. Either works but I personally prefer setting these to and running Clippy with before committing and on CI. This makes local iteration marginally easier because you can compile your code initially without fixing all the lints right away. Ultimately, as Clippy's docs say, "You can choose how much Clippy is supposed to annoy help you." But especially in the age of coding agents, I think it's worth tightening the guardrails so you end up with even fewer mysterious bugs in production and more code where you can say "if it compiles and lints, it should work." Discuss on r/rust , Lobsters , or Hacker News . - on (UTF-8 boundary panic). This would have caught my initial bug. / / - placeholder-panic macros - inside functions that return a - panics if the second is larger - / inside a function that returns a - drops without awaiting - swallows errors - silently drops - loses source error - discards the error message (only relevant if you're using an earlier edition than 2024) - deadlock pattern. The scoping was fixed in the 2024 edition so this is no longer an issue. - a that is too large can cause a stack overflow - every needs a comment - one unsafe op per block (one comment per op) / - only document safety where it belongs - on floats - stricter, also flags comparisons against constants - silently-rounded float literals ( ) - wraps to - always false - ( is single-threaded) - differs in debug vs release - method named returning non- - manual impl that disagrees with - impl whose error is should be - calls should be removed after debugging - every becomes - every requires a reason