1. 10
    The only true answer to 'tabs vs spaces' practices programming garrit.xyz
  1. 44

    Tabs have the problem that you can’t determine the width of a line, which makes auto-formatted code look weird when viewed with a different width. And configuring them everywhere (editor, terminal emulator, various web services) to display as a reasonable number of spaces is tedious and often impossible.

    1. 24

      I agree with you, tabs introduce issues up and down the pipeline. On GitHub alone:

      • diffing
      • are settings are per person or per repo
      • yaml or python, where whitespace is significant
      • what if you want things to line up, like comments, or a series of statements?
      • combinations of the above interacting

      If you’re turning this into, say, epub or pdf, would you expect readers and viewer apps to be able to adjust this?

      I fixed up some old code this week, in a book; tabs were mostly 8 spaces, but, well, varied chapter by chapter. Instead of leaving ambiguity, mystery, puzzling, and headaches for future editors and readers to trip over, I made them spaces instead.

      1. 8

        I don’t get the point about yaml and python. You indent with tabs, one tab per level, that’s it. What problems do you see?

        1. 4

          In the Python REPL, tabs look ugly. The first one is 4 columns (because 4 columns are taken up by the “>>> “ prompt), the rest are 8 columns. So you end up with this:

          >>> for n in range(20):
          ...     if n % 2 == 1:
          ...             print(n*n)
          
          1. 9

            When I’m in the Python REPL, I only ever use a single space. Saves so much mashing the spacebar and maintaining readability is never an issue as I’m likely just doing some debugging:

            >>> for n in range(20):
            ...  if n % 2 == 1:
            ...   print(n*n)
            
            1. 3

              True, but this shows that tabs don’t work well everywhere. Spaces do.

              1. 1

                Unless you use a proportional font.

                1. 2

                  Even with a proportional font, all spaces have the same width.

          2. 3
            def a():
            	x
                    y
            

            The two lines look the same, but they’re not to the python interpreter, even though you could use just spaces or just tabs.

            1. 17

              Don’t mix tabs and spaces for indentation, especially not for a language where indentation matters. Your code snippet does not work in Python 3:

              TabError: inconsistent use of tabs and spaces in indentation

              1. 1

                That was my point.

                1. 3

                  Your point is don’t mix tabs and spaces? Nobody proposed that. The comment you responded to literally states:

                  You indent with tabs, one tab per level, that’s it.

                  Or is your point don’t use tabs because if you mix in spaces it doesn’t work?
                  Then my answer is don’t use spaces, because if you mix in tabs it doesn’t work.

          3. 8

            what if you want things to line up, like comments, or a series of statements?

            https://nickgravgaard.com/elastic-tabstops/

            1. 2

              I appreciate that this is still surfaced, and absolutely adore it. I’d have been swayed by “tabs for indenting, spaces for alignment, for the sake of accessibility” if not for Lisp, which will typically includes indents of a regular tab-width off of an existing (arbitrary) alignment, such that indentation levels don’t consistently align with multiples of any regular tab-stops (eg. the spaces preceeding indention level 3 might vary from block to block depending on the context, and could even be at an odd offset). Elastic tab-stops seem like the only approach that could cator to this quirk, though I haven’t tried the demo with the context in mind.

              I also lament the lack of traction in implementations for Emacs, though it’s heartwarming to see those implementations that are featured. Widespread editor support may be the least of the hurdles to adoption, which feels like a third-party candidate in a two-party system. Does .editorconfig include elastics as an option? I’m not sure exactly how much work adding that option would entail, but that might be a great way to contribute to the preservation of this idea without the skills necessary to actually impliment support in an editor.

            2. 9

              what if you want things to line up

              Easy. Don’t.

              If you want to talk about diffing issues, then look at the diffs around half the Haskell community as a new value being longer requires a whole block to shift and either a bunch of manual manipulations or having to run a tool to parse and set your code just because you felt like things had to line up.

              1. 3

                what if you want things to line up, like comments, or a series of statements?

                Then you put spaces after your tabs. https://intellindent.info/seriously/

              2. 2

                I use tabs and autoformatters. I don’t think my code looks weird with any width between 2 and 8. What kind of weirdness do you refer to? About configuring, most developers have a dotfiles repo and manicure their setup there, why would setting a tabwidth there be more tedious than what most people do already anyway?

                1. 5

                  Let’s say that you have the maximum line length set to 24 columns (just to make the example clear). You write code like this:

                  if True:
                      print("abcdefg")
                      if False:
                          print("xyz")
                  

                  With the tab width set to 4 columns, your autoformatter will leave all lines alone. However, if someone has the tab width set to 8, the fourth line will overreach the limit. If they’re also using the same formatter, it will break up the fourth line. Then you’ll wonder why it’s broken up, even though it’s the same length as the second line, which wasn’t broken up. And your formatter might join it up again, which will create endless conflicts.

                  1. 4

                    Optimal line reading length is around 60 chars per line, not 60 characters including all leading whitespace. Setting bounds based on character from column 0 is arbitrary, and the only goal should be not too many characters per line starting at the first non-whitespace character (and even this is within reason because let’s be real, long strings like URLs never fit).

                    1. 3

                      Setting bounds based on character from column 0 is arbitrary

                      Not if you print the code in mediums of limited width. A4 paper, PDF, and web pages viewed from a phone come to mind. For many of those a hard limit of 80 columns from the start is a pretty good start.

                      1. 1

                        That is a fairer point as I was referring to looking at code in an editor–reason being that we’ve been discussing mediums where users can easily adjust the tab-width which is more on topic than static mediums. Web pages are the weird one where it should technically be just as easy to configure the width, but browsers have made it obnoxious or impossible to set our preferred width instead of 8 (I commented about it in the Prettier thread as people seem so riled up about it looking bad on GitHub instead of seeing the bigger picture that GitHub isn’t where all source code lives).

                        1. 5

                          Note that my favourite editor is the left half of my 13-inch laptop screen…

                    2. 1

                      I never really understood the need for a maximum length when writing software. Sure it makes sense to consider maximum line length when writing for a book or a PDF, but then it’s not about programming but about typesetting; you also don’t care about the programming font unless you’re writing to publish.

                      If you really want to set a maximum line length, I’d recommend to have a maximum line length excluding the indentation, so that when you have to indent a block deeper or shallower, you don’t need to recalculate where the code breaks.

                      But really don’t use a formatter to force both upper and lower limits to line lengths; sometimes it makes sense to use long lines and sometimes it makes sense to use short lines.

                      1. 5

                        Maximum line length makes sense because code is read more often than it’s written. In terms of readability, you’re probably right about maximum line length excluding indentation. But on the other hand, one of the benefits of maximum line length is being able to put multiple text buffers side-by-side on a normal monitor. Perhaps the very smart thing would be a maximum of 60 chars, excluding indentation, with a max of 110 chars including indentation. Of course, you have to treat tabs as a fixed, known width to do that.

                        1. 3

                          I never really understood the need for a maximum length when writing software.

                          There are a bunch of editing tasks for which I want to view 2 or 3 different pieces of code side by side. I can only fit so many editors side by side at a font size that’s still readable.

                          • looking at caller and callee
                          • 3 way diff views
                          • old and new versions of the same code
                          1. 3

                            Personally, I hate manually breaking up lines when they get too long to read, so that’s what an autoformatter is for. Obviously the maximum readable length differs, but to do it automatically, one has to pick some arbitrary limit.

                            1. 1

                              Sure, but there’s a difference between breaking lines when they get too long, and putting them together again when they are too short.

                              When I use black to format Python code, it always annoys me that I cannot make lines shorter than the hard limit. I don’t really care that I can’t make them longer than some arbitrary limit. Sure, the limit is configurable, but it’s per-file, not per-line.

                              If the problem you have is “where should I split this 120-character one-liner that’s indented with 10 tabs”, then tabs aren’t your problem.

                    3. 34

                      What’s the discovery here? That has always been the argument of “team tabs”

                      1. 23

                        Tabs for indentation, spaces for alignment.

                        1. 7

                          Exactly. Variable-width characters at the start of a line are great. Variable-width characters in the middle of a line are annoying because they won’t line up with other fixed-width things. Recent versions of clang-format now support this style, so there’s no reason to use spaces anymore.

                          1. 4

                            I have to suffer through clang-format at work, I can tell you it’s pretty bad. The worst aspect so far is that it does not let me chose where to put line breaks. It’s not enough to stay below the limit, we have to avoid unnecessary line breaks (where “unnecessary” is defined by clang-format).

                            Now to enforce line lengths, clang format has to assume a tab width. At my workplace it assumes 4 columns.

                            Our coding style (Linux) explicitly assumes 8.

                            1. 2

                              You can tell clang-format how wide it should assume for tabs. If people choose to use a wider tabstop value, then it’s up to them to ensure that their editor window is wider. That remains their personal choice.

                              1. 1

                                I’ve found out that clang-format respects line comments:

                                void f( //
                                    void *aPtr, size_t aLen, //
                                    void *bPtr, size_t bLen //
                                );
                                
                            2. 7

                              I think when people say this they imagine tabs will only occur at the start of the line. But what about code samples in comments? This is common for giving examples of how to use an function or for doc-tests. It’s much harder to maintain tab discipline there because your formatter would have to parse Markdown (or whatever markup you use) to know whether to use tabs in the comment. And depending on the number of leading comment characters, the indentation can look strange due to rounding to the next tabstop. Same thing goes for commented out sections of code.

                              1. 3

                                Go uses tabs for indentation and spaces for alignment. It works pretty well in practice. I can’t say that I’ve ever noticed anything misaligned because of it.

                                1. 4

                                  If you wrote some example code in a // comment, would you indent with spaces or tabs? If tabs, would you write //<space><tab> since the rest of the comment has a single space, or just //<tab>? gofmt can’t fix it for you, so in a large Go codebase I expect you’ll end up with a mix of both styles. With spaces for indentation it’s a lot easier to be consistent: the tab character must not appear in source files at all.

                                  1. 1

                                    I can’t say that I’ve ever written code in a comment, because I just write a ton of ExampleFunctions, which Go automatically adds to the documentation and tests. Those are just normal functions in a test file. I think what’s interesting about Go is that they don’t add all the features but the ones they do add tend to reinforce each other, like go fmt, go doc, and go test.

                                  2. 3

                                    Personally, I think it would have annoyed me if go fmt didn’t exist. Aligning code with spaces is annoying, and remembering to switch between them even more so.

                                    1. 1

                                      Yes, it’s only practical if a machine does it automatically.

                                2. 1

                                  I said this elsewhere in the thread, but it’s worth reiterating here: I’d bee with you 100% if it weren’t for Lisp, which simply can’t be idiomatically indented with tabs (short of elastic tabs) because it doesn’t align indentation with any regular tab-stops.

                                3. 21

                                  This is a clear and concise statement of the classic arguments in favor of tabs, but it doesn’t bring anything new to the table, and so can’t be considered an “answer” to the dispute.

                                  Both “tabs for indentation, spaces for alignment” and “electric tabstops” seem like good ideas, but to varying degrees they’re both dependent on tooling support (technically only electric tabstops requires editor support, but tfisfa requires either editor support or fanatical dedication). Spaces-only is the “worse is better” solution.

                                  1. 17

                                    Motivated by changes announced in the defaults of Prettier, the formatter mentioned in the article, Rich Harris tweeted something which made me reconsider my fairly radical (team spaces) stance in this debate, referencing this comment on GitHub.

                                    The tl;dr version is: refreshable braille displays work much better with tabs than they do with spaces.

                                    1. 9

                                      There’s other visual impairment issues tabs fix too.

                                      1. 17

                                        Ideally, I as a blind person would rather not deal with indentation at all. When I can, I let computers worry about that stuff for me. Quite a few of the totally blind programmers I’ve worked with couldn’t care less about this stuff. In a totally blind world, we’d use no indentation at all, or one-space-per-level indentation for whitespace sensitive languages, because that’s how a lot of us already work when left to our own devices. But in a blind world, whitespace sensitive languages would quickly go extinct…

                                        I don’t use a refreshable braille display to code, though if my hearing keeps deteriorating, I may well be forced to at some point. I have one for reading for pleasure. The thing I will say about them is that they have very little real estate. We’re talking one line of between 16 and 80 characters. That’s it. So indentation is a waste even there.

                                        When you read with your fingers, physical real-estate is at a premium. For comparison, if somebody had a copy of the Bible in braille, it would be a stack of books that is about the height I am. I’m a smidge over 1.6 meters tall.

                                        I don’t have answers for this stuff. Maybe some kind of structure editing. But I’d really love to foist all this stuff on the machines.

                                        1. 1

                                          But in a blind world, whitespace sensitive languages would quickly go extinct…

                                          Preferring to read minified code seems difficult regardless of display. How would code be laid out if there be a preference to not use indentation at all? The joke has always been that the semicolon-loving languages break and indent the same way whitespace-sensitive languages do anyhow so how do you format code to optimize for blind accessibility then??

                                          1. 1

                                            Preferring to read minified code seems difficult regardless of display.

                                            I did it for years. https://github.com/CMB/edbrowse/raw/master/perl/edbrowse.pl

                                            Minified code is not the same as code that doesn’t use whitespace to indicate structure.

                                            Paren / delimiter matching in editors is a big help too.

                                            How would code be laid out if there be a preference to not use indentation at all? The joke has always been that the semicolon-loving languages break and indent the same way whitespace-sensitive languages

                                            And what of the Lisps? There was a time when I would write Lisp breaking all of the sighted-Lisper formatting conventions, like, keeping parens on their own lines and uncuddled, to make it easier to navigate. That’s a form of using space to indicate structure, but it’s vertical not horizontal, and not dependent on the indentation level.

                                            There are conventions some blind Python programmers use to make their code easier to navigate, like “# endif blah” markers at the end of compound statements.

                                            1. 1

                                              Very interesting! Thanks for explaining.

                                      2. 1

                                        I wish accessibility software was smarter. A Braille display could compress whitespace itself, and it would be automatically better for all code, without needing the whole world to change.

                                        Similarly for screen readers I know a bunch of a11y tips like that: end your alt text with a full stop, don’t use more than one emoji, don’t use Unicode math symbols. Sigh.

                                      3. 25

                                        This has one huge downside though: everyone on the team has to agree, or live with this standard.

                                        That seems like the point of having a standard.

                                        1. 11

                                          The topicality guidelines literally refer to tabs vs. spaces as an example of an intractable off-topic debate. Speaking as someone who personally prefers tabs, I was hoping to at least see something new.

                                          1. 7

                                            It’s a mistake to have this conversation with your team. Just do the most default thing and spend zero energy on this. With the time you save, make the product better for users.

                                            Sometimes fussing over code patterns can benefit users—clear symbol names prevent errors, for instance—but if it’s got no downstream effects, it’s not worth the time investment.

                                            If the software exists not for its users but for the joy of coding, OK, knock yourself out.

                                            1. 1

                                              All true, but this is a discussion that everyone who’s ever programmed can have a low-effort opinion on. So it goes right to the top of popularity-based discussion boards like Lobsters (and reddit/USENET/HN).

                                              Popularity is a separate dimension from importance. Anyone that spends work time arguing and enforcing this stuff needs more work to do.

                                              BTW smart tabs are the true way, all other formatting schemes are inferior.

                                              1. 1

                                                Anyone that spends work time arguing and enforcing this stuff needs more work to do.

                                                I suspect it’s more that anyone who wants to argue this after a standard has been set is more interested in getting their way than getting on with work. Having an opinion is only natural (and can be amusing in some settings), thinking it’s more important than work is the problem.

                                            2. 7

                                              After 30 years I switched to tabs solely so I could make proportional fonts in code work better. Doing this means I give up horizontal alignment (say, for block comments) and it’s a lot easier with an automatic code formatter. But it looks so nice! Details here: https://nelsonslog.wordpress.com/2021/09/12/proportional-fonts-and-yapf-vs-black/

                                              The key observation is that tab is a semantic character, saying “this is a new level of indentation”, and the fact it’s 2 or 4 spaces shouldn’t matter at all

                                              1. 6

                                                This article conflates ‘alignment’, ‘formatting’, and ‘indentation’.

                                                Without covering the differences it doesn’t bring an light to the conversation. Yes editorconfig is cool and I recommend using it too, but since the article doesn’t even ask the right questions it cannot be trusted to have the only true answer.

                                                1. 4

                                                  Imagine “tabs only” for lisp:

                                                  ((foo)
                                                          (bar))
                                                  

                                                  😭

                                                  1. 3

                                                    You could set your tab size to 1. I’m not saying you should, but you could. And with the EditorConfig in place per the article that would stick across many editors and even code hosting sites.

                                                    1. 1

                                                      If you don’t like how big your tabs are, you are free to make them smaller. That’s kind of the whole point

                                                      1. 1

                                                        Lisp alignment offsets are variable, unlike traditional indentation. So no single fixed number can work.

                                                        You’d need something like Graavgard’s elastic tab stops.

                                                        1. 0

                                                          Or write lisp with sensible indentation… Most lisp uses whitespace as obfuscation I find…

                                                  2. 6

                                                    In order to gain wisdom on this topic, one must meditate, breathe deeply and then watch S03E06 of Silicon valley. After that, one will be enlightened, unfettered and unburdened.

                                                    1. 5

                                                      Another year, another “definitive” answer. Here’s one from a long time ago: https://www.jwz.org/doc/tabs-vs-spaces.html. I’m sure you can find a dozen more, all pretending their answer is the only one making sense.

                                                      I recently dove into a new project at work. We’re starting from a blank page, so of course this classic question came up:

                                                      “So should we use tabs or spaces for our formatting?”

                                                      Your $WORK should hire more senior people, with a developed aversion to discussing meaningless “classic” questions.

                                                      1. 4
                                                        1. Determine if end result of choice is arbitrary
                                                        2. If so, pick one of the arbitrary choices and live it.

                                                        IMO this advice works for quite a lot. We often use arbitrary systems to ensure consistent process (using Fibonacci sequence for task grooming). However people often get caught up that their arbitrary process is the true correct way. It’s not.

                                                        1. 4

                                                          Is there any reason why an editor couldn’t do the same for leading spaces?

                                                          A project is configured to use 4 spaces per indentation level, my preference is 8 spaces, editor displays each leading space as 2 spaces.

                                                          If I cared more about it I would give it a try.

                                                          1. 1

                                                            This would break alignment at least

                                                            1. 2

                                                              So there are auto formatters now, which are pretty neat. I wonder if the next level is running the auto formatter both on opening the file as well as saving it (or checkout and checkin, etc). This way you can edit with whatever your personal readability preferences are, while also keeping the code in a consistent format.

                                                              1. 1

                                                                Now that editors use things like Tree-sitter, the old idea of storing a syntax tree, that you can render however you want, feels close.

                                                                And the serialised format on disk is just the auto-formatted plain-text source-code.

                                                          2. 3

                                                            The reason this debate continually comes up and there can never be a resolution to it is that there isn’t a lowest common denominator solution. Both solutions have roughly equivalent upsides and downsides. Both solutions require full buy in across the team.

                                                            Both solutions make a claim to be the lowest common denominator solution. The only thing that matters is that any given team pick one side for their codebase and stick with it. No matter which one you pick someone will disagree so it doesn’t matter which. Flip a coin if you want.

                                                            1. 1

                                                              I totally agree with this. As long as the decision is reasonable (and in case of tabs/spaces both are) it’s more important to have a common standard. We recently had to decide for a Python formatting standard and just chose Black to avoid discussions. Then we can focus on business problems.

                                                            2. 2

                                                              If a team can’t agree on indentation level, they probably have disagreements over other issues and there may be a difficult dynamic at work.

                                                              The team should agree on an approach. I get that bikesheds get everyone involved, but that’s actually a good thing. If the team can’t resolve their differences on issues like this, they will have more challenging difficulties ahead.

                                                              If people think that they are special and don’t need to accommodate the team, that’s a dynamic that perhaps needs to be worked on.

                                                              (I was of the opinion that the golang gofmt tool was “wrong” by my standards (my personal issue was “cuddled elses”). Took me very little time to get over it. Happiness with autoformatting since then.)

                                                              1. 1

                                                                The true answer is that is is a false dichotomy and you should use both

                                                                1. 1

                                                                  Formatting is not as easy as it looks like.

                                                                  /* Token types returned by nexttoken() */
                                                                  enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT,
                                                                         tUNUMBER, tZONE, tDST };
                                                                  

                                                                  How would you format the third line? Would you use tabs? If yes, then you would use them wrong and you shouldn’t use tabs at all.

                                                                  Tab formatting is most complicated, mostly because it has to be supplemented with spaces sometimes.

                                                                  1. 1

                                                                    Whenever someone writes an article or post with this title, their answer is always tabs. I’ve seen this more than a few times over the past 25+ years. Yet many of us continue to prefer spaces, except when writing Go. 😹