6 ms·
Tiger Style: Coding philosophy (2024)
- userbinator 11mo agoLimit function length: Keep functions concise, ideally under 70 lines. Shorter functions are easier to understand, test, and debug. The usual BS... yes, shorter functions are easier to understand by themselves but what matters, especially when debugging, is how the whole system works. Edit: care to refute? Several decades of experience has shown me what happens. I'm surprised this crap is still being peddled.
- danparsonson 11mo agoThe low level stuff is also important.
- stouset 11mo agoIt’s a lot easier to understand entire systems when they’re composed of small, pure, straightforward parts.
- vlovich123 11mo agoYeah, you can’t build a building unless you’re certain that every brick, rebar, and cement is all solid and meets a quality bar. Of course you need architectural schematics to make sense of the broader picture, but you need quality control on the pieces being put in place too.
- userbinator 11mo agoNo it isn't. The whole system should be small.
- puchatek 11mo agoThis one, like some others in this style guide, can also be found in Clean Code. Not sure why you feel the need to call it "BS". Nobody is saying that your 75 line function is bad. It's a reasonable guideline. Juniors won't do this automagically.
- Kwpolska 11mo agoThe "Clean Code" book is famous for making bad suggestions and producing unreadable code with too many functions just for the sake of following its rules.
- userbinator 11mo agoIt's worth noting that the author of "Clean Code" does not appear to have written any software of significance. He's nothing more than a preaching salesman. There are far more real developers to learn good practices from. Brian Kernighan, Dennis Ritchie, Linus Torvalds, etc.
- jval43 11mo agoYes and it was BS in Clean Code too, not a fan. Don't get me wrong, I often apply it myself and refactor code into smaller functions. But readability, understandability and ease of maintenance comes first. Especially juniors will apply such rules overzealously, bending over backwards to stay within an arbitrary limit while at the same time not really understanding why. Frustratingly, their lack of experience makes it impossible to discuss such dogmatic rules in any kind of nuanced way, while they energetically push for stricter linter rules etc. I've tried, and one even argued there's never ever a reason to go over N lines because their book is "best-practice" and said so. And you should not deviate from "best-practice" so that's it. I'm not making this up! That said, I'm always open to discuss the pros and cons for specific cases, and I do agree the default should be to lean towards smaller functions in 90% of cases.
- bccdee 11mo agoClean Code is different. Clean Code suggests 2-5 line functions. A 70-line limit is worth considering; 5 lines is not.
- baalimago 11mo agoI would not hire a monk of TigerStyle. We'd get nothing done! This amount of coding perfection is best for hobby projects without deadlines.
- stouset 11mo agoThis seems pretty knee-jerk. I do most of this and have delivered a hell of a lot of software in my life. Many projects are still running, unmodified, in production, at companies I’ve long since left. You can get a surprising amount done when you aren’t spending 90% of your time fighting fires and playing whack-a-mole with bugs.
- baalimago 11mo agoWell, I'm sure you're well aware of perils of premature optimization and know how to deliver a product within a reasonable timeframe. TigerStyle seems to me to not be developed through the lens of producing value for a company via software, but rather having a nice time as a developer (see: third axiom). I'm not saying the principles themselves are poor, but I don't think they're suitable for a commercial environment.
- stouset 11mo agoWhich ones, specifically?
- SatvikBeri 11mo agoTigerStyle is developed in the context of TigerBeetle, who seem to be a successful company getting a lot done.
- baalimago 11mo agoThen I'm curious about what their principles on deadlines. I don't see how it aligns with their coding styleguide. Taking the TigerStyle at face value does not encourage deliverance. They're practically saying "take the time you need to polish your thing to perfection, 0 technical debt". But ofc, I understand styleguides are... well.. guides. Not law.
- deleted 11mo ago[deleted]
- andai 11mo agoRelated: https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TIGER_STYLE.md https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TI...
- keyle 11mo agoI was enjoying what I was reading until the "Limit function length" part which made me jolt out of my chair. This is a common misconception. Limit function length: Keep functions concise, ideally under 70 lines. Shorter functions are easier to understand, test, and debug. They promote single responsibility, where each function does one thing well, leading to a more modular and maintainable codebase. Say, you have a process that is single threaded and does a lot of stuff that has to happen step by step. New dev comes in; and starts splitting everything it does in 12 functions, because, _a function, should do one thing!_ Even better, they start putting stuff in various files because the files are getting too long. Now you have 12 functions, scattered over multiple packages, and the order of things is all confused, you have to debug through to see where it goes. They're used exactly once, and they're only used as part of a long process. You've just increased the cognitive load of dealing with your product by a factor of 12. It's downright malignant. Code should be split so that state is isolated, and business processes (intellectual property) is also self contained and testable. But don't buy into this "70 lines" rule. It makes no sense. 70 lines of python isn't the same as 70 lines of C, for starters. If code is sequential, and always running in that order and it reads like a long script; that's because it is! Focus on separating pure code from stateful code, that's the key to large maintainable software! And choose composability over inheritance. These things weren't clear to me the first 10 years, but after 30 years, I've made those conclusions. I hope other old-timers can chime in on this. The length of functions in terms of line count has absolutely nothing to do with "a more modular and maintainable codebase", as explained in the manifesto. Just like "I committed 3,000 lines of code yesterday" has nothing to do with productivity. And a red car doesn't go faster.
- stouset 11mo ago“Ideally under 70 lines” is not “always under 70 lines under pain of death”. It’s a guideline. There are exceptions. Most randomly-selected 100-line functions in the wild would probably benefit from being four 25-line functions. But many wouldn’t. Maturity is knowing when the guideline doesn’t apply. But if you find yourself constantly writing a lot of long functions, it’s a good signal something is off. Sure, language matters. Domain matters too. Pick a number other than 70 if you’re using a verbose language like golang. Pick a number less if you’re using something more concise. People need to stop freaking out over reasonable, well-intentioned guidelines as if they’re inviolable rules. 150 is way too many for almost all functions in mainstream languages. 20 would need to be violated way too often to be a useful rule of thumb.
- mjlawson 11mo agoZero technical debt certainly is... ambitious. Sure, if we knew _what_ to build the first time around this would be possible. From my experience, the majority of technical debt is sourced from product requirement changes coupled with tight deadlines. I think even the most ardent follower of Tiger Style is going to find this nigh impossible.
- zoul 11mo agoI would even say that from a project management perspective, zero technical debt is undesirable. It means you have invested resources into perfecting something that, almost by definition, could have waited a while, instead of improving some more important metric such as user experience. (I do understand tech debt makes it harder to work with the codebase, impacting all metrics, I just don’t think zero tech debt is a good target.)
- lll-o-lll 11mo ago> perfecting something that, almost by definition, could have waited a while No technical debt is not the same thing as “perfection”. Good enough doesn’t mean perfect. Would it be ok to submit an essay with only 90% of the underlined spelling mistakes fixed? Do you paint your outdoor table but leave the underside for later? Do it once, do it right. That doesn’t mean perfect, it means not cutting corners.
- zoul 11mo agoWould you keep fixing the underlined spelling mistakes on your “watch out for holes in the pavement” sign while people are already walking there?
- lll-o-lll 11mo agoThere are contexts where quick and dirty and (hopefully) come back later are warranted. Far more often it is just an excuse for shoddy work. You used the word “perfection” as the contrast to “technical debt”. Granted, technical debt is not a well defined term, but I am simply highlighting that “free from technical debt” in no way implies anything like perfect. It just implies well made.
- eviks 11mo ago> Limit line lengths: Keep lines within a reasonable length (e.g., 100 characters) to ensure readability. This prevents horizontal scrolling and helps maintain an accessible code layout. Do you not use word wrap? The downside of this rule is that vertical scrolling is increased (yes, it's easier, but with a wrap you can make that decision locally) and accessibility is reduced (and monitors are wide, not tall), which is especially an issue when such a style is applied to comments so you can't see all the code in a single screen due to multiple lines of comments in that long formal grammatically correct style Similarly, > Limit function length: Keep functions concise, ideally under 70 lines. > and move non-branching logic to helper functions. Break accessibility of logic, instead of linearly reading what's going on you have to jump around (though popups could help a bit). While you can use block collapse to hide those helper blocks without losing their locality and then expand only one helper block.
- skydhash 11mo ago> which is especially an issue when such a style is applied to comments so you can't see all the code in a single screen due to multiple lines of comments in that long formal grammatically correct style It’s easier to hide comments than do code wrapping correctly. And comments are usually in a lighter color than code and easy to skip over. > Break accessibility of logic, instead of linearly reading what's going on you have to jump around (though popups could help a bit) Extracting function is for abstraction, instead of deciphering some block and tracking its entanglement, you have a nice name and a signature that document its input and output. And maybe a nice docstring that summarizes its purpose. It does require taste and design skill.
- eviks 11mo ago> a lighter color than code and easy to skip over. How does color and the ease of skipping address the fact that you don't see code because it's pushed off screen? Especially compared to the ease of skipping when you wrap the whole paragraph of comments into a single long line when you don't need to read it. > And maybe a nice docstring that summarizes its purpose. Which you could just as well have right there where you read the block. The nice name/signature yes, that can be situationally better
- nilirl 11mo agoCan you have a coding philosophy that ignores the time or cost taken to design and write code? Or a coding philosophy that doesn't factor in uncertainty and change? If you're risking money and time, can you really justify this? - 'writing code that works in all situations' - 'commitment to zero technical debt' - 'design for performance early' As a whole, this is not just idealist, it's privileged.
- titanomachy 11mo agoTheir product is a high-throughput database for financial transactions, so they might have different design requirements than the things you work on.
- nilirl 11mo agoYes, my point is it's a privilege to have an engineering philosophy that doesn't need to address time spent, monetary cost, or uncertainty.
- benrutter 11mo agoI would argue that these: - 'commitment to zero technical debt' - 'design for performance early' Will save you time and cost in designing, even in the relatively near term of a few months when you have to add new features etc. There's obviously extremes of "get something out the door fast and broken then maybe neaten it up later" vs "refactor the entire codebase any time you think soemthing could be better", but I've seen more projects hit a wall due to leaning to far to the first than the second. Either way, I definitely wouldn't call it "privileged" as if it isn't a practical engineering choice. That seems to judt frame things in a way where you're already assuming early design and commitment to refactoring is a bad idea.
- nilirl 11mo agoYour argument hinges on getting the design right, upfront. That assumes uncertainty is low or non-existent. Time spent, monetary cost, and uncertainty, are all practical concerns. An engineering problem where you can ignore time spent, monetary cost, and uncertainty, is a privileged position. A very small number of engineering problems can have an engineering philosophy that makes no mention of these factors.
- teo_zero 11mo agoWhat has always baffled me is how CS uses the word "safety" where all other industries use "robustness". A robust design is one that not only is correct, but also ensures the functionality even when boundary conditions deviate from the ideal. It's a mix of stability, predictability and fault tolerance. Probably "reliable" can be used as a synonym. At the same time, in all industries except CS "safety" has a specific meaning of not causing injuries to the user. In the design of a drill, for example, if the motor is guaranteed to spin at the intended rpm independently of orientation, temperature and state of charge of the battery, that's a robust design. You'll hear the word "safe" only if it has two triggers to ensure both hands are on the handles during operation.
- benrutter 11mo agoI think in things like philosophy/debate/etc it's common to talk about "safe assumptions". I always figured that's the metaphor being leaned on here. I agree it's confusing when you step outside of CS world though.
- inejge 11mo ago> What has always baffled me is how CS uses the word "safety" where all other industries use "robustness". FWIW "safety factors" are an important part of all kinds of engineering. The term is overloaded and more elusive in CS because of the protean qualities of algorithmic constructs, but that's another discussion.
- psychoslave 11mo ago> At the same time, in all industries except CS "safety" has a specific meaning of not causing injuries to the user. That makes sense, as CS is transversal to industries. Same practices that can contribute to literally save lifes in one domain will just avoid minor irritating feeling in an other.
- jillesvangurp 11mo agoYou use safety more in relation to correctness aspects of algorithms. Some safety properties you can actually prove. When it comes to robustness it is more about dealing with things sometimes being incorrect regardless of the safety mechanisms. So, a try catch for something that isn't actually expected to normally fail makes you robust against the scenario when that does fail. But you'd use e.g. a type system to prevent classes of failures as safety mechanism. It's a very soft distinction, I agree. And possibly one that translates less well to the physical world where wear and tear are a big factor for robustness. You can't prove an engine to be safe after thousands of hours. But you can make it robust against a lot of expected stuff it will encounter over those hours. Safety features tend to be more about protecting people than the equipment.
- benrutter 11mo agoI like this, to me it reads like a collection of fairly obvious best practices (even if there might be practical reasons to avoid when shipper fast etc), so I'm surpised to see so many enraged comments. Any recommendations for other coding philosophies or "first principle" guides? I know of "extreme programming" but not much else.
- ad_hockey 11mo agoA Philosophy of Software Design by John Ousterhout is often recommended, and is very good. There was an interesting debate between John and Uncle Bob on their differences in style recently[1], with a related HN discussion[2]. [1] https://github.com/johnousterhout/aposd-vs-clean-code/blob/main/README.md https://github.com/johnousterhout/aposd-vs-clean-code/blob/m... [2] https://news.ycombinator.com/item?id=43166362 https://news.ycombinator.com/item?id=43166362
- WillAdams 11mo agoI would like to note that this text was transformative in my current project (an effort to model CNC work in 3D) --- when I first read the book, I did it chapter-by-chapter, applying the lesson learned from that chapter to the entire codebase before moving on to the next --- I am currently re-reading the book for the third time, and I'm planning on a compleat re-write before I do 1.0....
- stelldichein 11mo agothis smells like Clean Code BS. no real substance either, I could have made ChatGPT generate this article
- exceptione 11mo agoPretty good list, but a hidden assumption is that the reader works in an imperative style. For instance, recursion is the bread and butter of functional and logical programming and is just fine. The most important advice one can give to programmers is to 1. Know your problem domain. 2. Think excessively deep about a conceptual model that captures the relevant aspects of your problem domain. 3. Be anal about naming your concepts. Thinking about naming oftentimes feeds back to (1) and (2), forming a loop. 4. Use a language and type system that is powerful enough to implement previous points.
- bfLives 11mo agoI got this sense as well, particularly from the section about assertions. Most of the use cases they describe (e.g checking function arguments and return values) are much better handled by an expressive type system than ad-hoc checks.
- AnimalMuppet 11mo agoIf you can, yes. But that last 10% of checking may be really hard to encode in types. It may be especially hard to do so in the language that you want to use for other reasons.
- andsoitis 11mo ago> Avoid recursion if possible to keep execution bounded and predictable, preventing stack overflows and uncontrolled resource use. In languages with TCO (e.g. Haskell, Scheme, OCaml, etc.) the compiler can rewrite to a loop. Some algorithms are conceptually recursive and even though you can rewrite them, the iterative version would be unreadable: backtracking solvers, parsing trees, quicksort partition & subprblems, divide-and-conquer, tree manipulation, compilers, etc.
- rerdavies 11mo agoPresumably why it says "Avoid recursion if possible".
- andsoitis 11mo ago> Allocate all necessary memory during startup and avoid dynamic memory allocation after initialization. Why?
- Cwizard 11mo agoYou can run out of memory and trigger a crash.
- Manfred 11mo agoI think they are hinting at the idea that a long running process on a dedicated server should allocate all necessary memory during initialization. That way you never accidentally run out of memory during high loads, a time when you really don't want your service to go down. You would need so much more physical memory with this approach. For example, you may want to allocate a lot of memory for the browser, but then close the tab. 2 seconds later you want to use it for a compiler. And then you switch to a code editor.
- alextingle 11mo agoTwo reasons. 1. Dynamic memory allocation is error prone. Mixing it in with your control flow makes that hard to manage. Many of the strategies for keeping track of dynamic memory add significantly to the complexity of the program... You need to consider object ownership, maybe introduce reference counting, even garbage collection, or "borrow checkers". If you can avoid all of that by making good architectural choices up front then your code will be much simpler and robust. 2. Dynamic allocation is slow, especially in a multithreaded environment. It's doubly slow if you layer garbage checking or whatever on top of it. Prefer fewer trips to the allocator, preferably none in your code's hot path.
- MrBuddyCasino 11mo agoThings an engineer early in his career would write.
- rerdavies 11mo agoOr things a senior engineer would write late in their career to make it easier to train junior programmers. <shrugs>
- adfawlolass 11mo agoTigerBeetle is not early engineer lol
- cjfd 11mo agoThis is the kind of development that one needs for safety critical applications. E.g., nuclear power plants or airplane control software. I don't think it is economically feasible for less critical software. It presumes a great degree of stability in requirements which is necessary for such applications.
- bccdee 11mo agoIndeed, a lot of it is pulled from NASA's "Power of Ten – Rules for Developing Safety-Critical Code." The original tigerbeetle doc cites them explicitly: https://github.com/tigerbeetle/tigerbeetle/blob/ac75926f8868093b342ce2c64eac1e3001cf2301/docs/TIGER_STYLE.md#safety https://github.com/tigerbeetle/tigerbeetle/blob/ac75926f8868...
- jakubmazanec 11mo ago> Do it right the first time: Take the time to design and implement solutions correctly from the start. Doing good design is off course important, but on the other hand software design is a lot of times iterative because of unknown unknown s. Sometimes it can be better to create quick prototype(s) to see which direction is the best to actually "do it right", instead of spending effort designing something that in the end won't be build.
- andersmurphy 11mo agoThe orginal piece is a much more nuanced read, I feel this one misses some context. https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TIGER_STYLE.md https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TI...
- aranw 11mo agoThe attribution to TigerBeetle should be at the top of the page with a link to the original tigerstyle, not buried at the bottom. Right now it reads like official TigerBeetle content until you scroll down, which isn't fair to either you or the original team.
- jorangreef 11mo agoThe author graciously gifted the domain to us and we’re literally days away from launching original TigerStyle here.
- andersmurphy 11mo agoOh that's very nice of them.
- aranw 11mo agoThat's really nice of the author and awesome to hear. Would love to see TigerStyle be published in a similar format and expanded upon with more detail be great for referencing with my colleagues. Would love to adopt it on projects or some of the rules at least
- redhale 11mo agoI literally laughed out loud when I got to this line > Do it right the first time So easy, why didn't I think of that!? /s Reminds me of the mental health meme of telling depressed people to just be happier instead. I don't necessarily disagree with a lot in this philosophy, but much of it is puffery if not accompanied by practical positive and negative examples. If a junior with little experience reads this, I'm not sure if they'll be better or worse off. For example, "Design for performance early" is dangerous if it leads to premature optimization. But that's not mentioned. Practical positive and negative examples that illustrate the balance between these two concerns would make the advice actionable.
- adfawlolass 11mo ago> If a junior with little experience reads this Won't happen. This is for TigerBeetle.
- ramses0 11mo agoInteresting use of `latency_ms_max` as a naming convention. I'm definitely guilty of `max_latency_ms` instead, but they make a convincing argument for putting `max` at the end. If this topic floats your boat, go look up the NASA coding standards. For a few projects, I tried to follow a lot of their flow control recommendations, and will still reach for: `while ... && LIMIT > 0` in some situations. Still a huge fan of including some type info in the variable name, eg: duration_s, limit_ms makes it extremely clear that you shouldn't mix math on those integers.
- huqedato 11mo ago"Zero technical debt" - I doubt this is feasible in practice.
- harlequinetcie 11mo agoI'm not even clear on what it means, technical debt is non-deterministic in many cases. To say it differently: if you wrote code that was perfect in time 0, that code may become legacy in time 100. Are they saying you should continuously refactor all your code to cover the 'current user needs'? I just think it's an oversimplification for those cases where you don't mind not covering the 0,001% of use cases.
- dionian 11mo agoEven if it is, it sounds highly ineffective, unless the only value you are delivering is source code.
- binary132 11mo agoThis just makes me want to write sloppy dangerous throwaway code with wild abandon
- mkornaukhov 11mo ago> Allocate all necessary memory during startup and avoid dynamic memory allocation after initialization. Absolutely not an ultimatum advice, even harmful in most cases. Just measure you memory usage and don't rely on and avoid external OOM handler.
- jackfranklyn 11mo ago[flagged]
- bccdee 11mo ago> Each step modifies state that the next step needs. I've been bitten by this. It's not the length that's the problem, so much as the surface area which a long function has to stealthily mutate its variables. If you have a bunch of steps in one function all modifying the same state, there's a risk that the underlying logic which determines the final value of widely-used, widely-edited variables can get hard to decipher. Writing a function like that now, I'd want to make very sure that everything involved is immutable & all the steps are as close to pure functions as I can get them. I feel like it'd get shorter as a consequence of that, just because pure functions are easier to factor out, but that's not really my objective. Maybe step 1 is a function that returns a `Step1Output` which gets stored in a big State object, and step 2 accesses those values as `state.step1Output.x`. If I absolutely must have mutable state, I'd keep it small, explicit, and as separate from the rest of the variables as possible.
- jackfranklyn 11mo ago[flagged]
- bccdee 11mo ago> harder to trace when you're debugging "why did this transaction get coded wrong" and the answer spans 6 different step outputs That's definitely a pain, but I'm not sure it's easier when this is one variable being mutated in six different places. I think you're just running into the essential complexity of the problem.
- jackfranklyn 11mo ago[flagged]
- le-mark 11mo ago
- norir 11mo agoMaster recursion and there will be nothing left to master. Avoid recursion and you will remain forever stuck in a loop.
- thomascountz 11mo agoLots of debating about the color to paint the fence in the design meetings for the nuclear reactor... I don't know how this philosophy is applied at TigerBeetle. When I establish engineering guidelines I try to frame them as exactly that: guidelines. The purpose is to spawn defensible reasoning, and to trigger reflection. For example, I might say this: We use a heuristic of 70 lines not as a hard limit, but as a "tripwire." If you cross it, you are not 'wrong,' but you are asked to pause and consider if you're introducing unintentional complexity. If you can justify it, keep it—there's no need to code golf. "Style," "philosophy," "guides," they're all well-meaning and often well-informed, but you should be in command of the decision as the developer and not forget your own expertise around cohesion, cognitive load, or any functional necessities. There are staunch believers in gating deploys based solely on LOCs, I'm sure... I like the idea of finding ways to transparently trigger cognitive provocations in order for everyone to steer towards better code without absolutes.
- dionian 11mo agoa zero technical debt approach is good sometimes, but im not sure i would take it this far. It depends.
- abhashanand1501 11mo agoI like the 100% philosophy for coding: 1. 100% code coverage 2. 100% branch coverage 3. 100% lint (without noqa) 4. 100% type check pass(for python/js) 5. 100% documentation coverage 6. All functions with complexity less than 5. All functions with no of lines less than 70. All files with number of lines less than 1000. These make code high quality, and quality of life is directly proportional to qualify of your code.
- Terretta 11mo agoSee also: Rugged Manifesto “Rugged” describes software development organizations that have a culture of rapidly evolving their ability to create available, survivable, defensible, secure, and resilient software. https://ruggedsoftware.org/ https://ruggedsoftware.org/ https://github.com/rugged-software/rugged-software.github.io https://github.com/rugged-software/rugged-software.github.io
- cadamsdotcom 11mo agoThis advice is great but lacks context. For many shops it’s too much effort for the payoff. Unless you work on medical devices or aerospace then maybe. Your situation will warrant some of these things, or doing them to a lesser degree. Part of our role is to decide and recommend what’s appropriate.
- bicepjai 11mo agoJust curious, is this something that evolved trying to tame agentic coding with rules ? because it does feel like something I will put in my CLAUDE.md
- hairdrop 11mo agoNot to midwit but I will stick to MISRA C for this sort of thing. It is very clear in why each thing must be done and it is hard to argue against most of it. It’s usually where safety and robustness deeply matter and using it would have avoided a number of high-profile exploits we have seen in the recent past. While not every “rule” may be applicable to all languages, many standard languages could adopt a good number of rules from it and benefit greatly. Anyone with a number of years experience with some reflection on what is good, clear and unambiguous style would reinvent a decent portion of MISRA C. I’ll let others speak to the Barr standard.