Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I think I mention this all the time when this comes up, but I learned the most 'best practices' through using ruff.

https://docs.astral.sh/ruff/

I just installed and enabled all the rules by setting select = [ "ALL" ]

And then looked at the 'errors' it showed me for my existing code base and then excluded some rules which don't interest me.

I also have it set up in my IDE, so it'll show me the linting errors while coding.

In a larger code base there will definitely be many 'errors', but using it cleaned up my code really good and it stopped me from some footguns.



This is good advice. I learned Python long before ruff came on the scene, but I did the same with Pylint. I don't adhere rigidly to its recommendations any more, but I learned a lot about the language from trying. I fact, I think some of its recommendations are downright wrong, and what I learned was that I made my code harder to maintain by following them.


I'm curious what recommendations you remember disagreeing with.


I can't say I've seen any but some of the code style ones are very prescriptive (function longer than N lines, short variable names etc.).

Single letter variable names are totally fine in some cases. And while very long functions may be bad, it's pretty annoying when you're adding one line to a function for the linter to say "nope. have you considered dropping everything and refactoring this?"

You can easily turn them off though. I can't remember any code based ones that are really wrong.

Maybe some are prone to false positives, e.g. warning about a default `= []` argument. But you can waive them individually.


Yeah, it's the "refactoring" category where I disagree most. Too many identifiers in a function, too many lines in a function, too many branches, too many methods in a class, too few methods in a class. You can learn a lot by "fixing" these, but sometimes there's no elegant fix. Take "too many branches." Sometimes you just have to write a function with 90 lines of

    if foo(x):
        return bar(y)
    if baz(x):
        return quux(y)
    ...
Sure, there are tricks you can play. You can make the conditions hierarchical and subdivide. You can evaluate all of the conditions first and reduce the function to a lookup. You can define 45 subclasses and define a polymorphic method "frob" on x such that all you have to do is call

    x.frob(y)
I can probably think of half a dozen others. Sometimes it's a good idea, but sometimes you're just better off with a bug dumb function full of straightforward conditionals. It's worth listening to Pylint to learn all of these tricks, so that you can confidently ignore its advice when there really isn't a better way.


The value of this will vary wildly based on experience level. I wouldn’t suggest this outside of fairly senior developers.

Without a very good grasp of the language, it can be counterproductive to deep learning. The linters teach what to do, but not why, so it’s hard to grasp how the codebase is worse without following the linter, or when it’s appropriate to disable a linter vs dealing with it.

Ie I’m not a great JS dev. When the linter says “this should be an arrow function” I understand how to do that, but not why an arrow function is preferable there or in general. I would probably be a better JS dev if I hadn’t had the linter, felt whatever pain from not using arrow functions, and know why they’re important. Or never feel the pain and realize it’s a style choice more than a functional one.

I prefer using them to help me remember things I already understand. I know why mutating a copy of a string doesn’t mutate the caller’s copy, so I’m happy to have a linter point out when I’m trying to do that inadvertently.


> The linters teach what to do, but not why

Ruff specifically actually has a web page for every single check with a section on the rationale behind it.

I regularly use this when I see an error that I don't understand the purpose of, and am often convinced, though sometimes I recognize that the thing it's trying to protect me from doesn't apply in my particular case and so I disable it. Regardless. I feel it has had the effect of making me a better developer.


Yes, this is a big part of what makes this approach tolerable. In VSCode, the on-hover tooltip includes a link the ruff rule page with this information, so there's no searching needed.

I've come to rely on that so much, it's really annoying when the error is from mypy, whose tooltips do not have such links.

Patiently waiting for Astral's mypy-killer!


I agree that sometimes linters can enforce code styles that are more of hassle to deal with than offer any real concrete gain to new developers. But I disagree that only senior developers should use linters. Especially if you are learning a new language, it can introduce you to common conventions in that language, writing cleaner and more idiomatic code, and helps form good habits off the jump instead of building bad habits you will eventually have to change in a professional setting. Sure it can be overzealous at times, but I think on the whole it is a net positive.


> But I disagree that only senior developers should use linters.

I'm on the same boat. I started using python ~1 year ago, because it is the main language I use at my dayjob. And I didn't really use python before this (although I was already proficient in other languages).

In the beginning my code was very messy and I spent much time searching for how to do things the 'correct' way.

And ruff made this so much easier, and it made me look at some python topics more thoroughly. And now I'd say I have a very good understanding of python and its best practices, and I'm now one of the most proficient python developers in my department (it's not a high bar, we have many data scientists, which are most of the time only proficient in their libraries/tooling they use, and I'm one of the few that is not a data scientist).

I'm not saying that solely ruff was the reason I'm now in proficient in python, but it made it easier + I would have never looked into some things without it.

For example, I also type my python code, and before I used ruff I had many problems with circular dependencies. But ruff could fix it with a simple automatic fix by using from __future__ import annotations and if TYPE_CHECKING.

And the ruff documentation also gives more explanation on the why and how for most of their rules, which is also very valuable.

https://docs.astral.sh/ruff/rules/future-rewritable-type-ann...

https://docs.astral.sh/ruff/settings/#lint_flake8-type-check...


To me, keeping the linter and the type checker happy is almost as important as actually writing good code.

If you don't know how to use the tools, you're missing out on time savings and error prevention, and once you write code that the linter doesn't like, you can't make it compatible without significant manual work.

I almost think Python would be better with mandatory types....




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: