7 ms·
I loved this paragraph: > This dichotomy gels really well with the way my brain works. I’m able to channel short bursts of creative energy into precisely mappi
by digging 2y ago
I loved this paragraph:
> This dichotomy gels really well with the way my brain works. I’m able to channel short bursts of creative energy into precisely mapping the domain or getting type scaffolding set up. And then I’m able to sustain long coding sessions to actually implement the feature because the scaffolding means I rarely have to think too hard.
It's why I keep begging my team, every time there's a new codebase (or even a new feature), to stop throwing `any` onto everything more complicated than a primitive. It is exhausting. It forces me to waste energy on the shitty, tedious parts. It forces me to debug working code just to find out how it works before I can start my work.
They tend to take the quickest solution to everything -- which means everyone else has to do the same work over and over again until someone (me, invariably) sits down and makes a permanent record of it.
In doing this they ensure that I can't trust any of their code, which is counterproductive for what should be obvious reasons. Every time I work on established, untyped (or poorly typed) code, it's like I'm writing new code with hidden, legacy dependencies.
- ervine 2y agoWhy is `any` allowed at all? Enable strict mode, set up your linter, don't allow any implicit or explicit `any` anywhere. Without this, Typescript is next to useless. Not knowing if the types are good is worse than no types at all.
- phyrex 2y agoProgressive typing of an untyped code base. Types that are too complex to represent in that type system.
- t-writescode 2y ago* Common functions such as parsing functions in languages that don't support function overloading * "equals" and other global functions.
- shepherdjerred 2y agoTypescript have solutions for both of those problems: conditional types and generics
- ervine 2y agoYep, adopting strict after the fact is a different conversation, but one that has been talked about a bunch and there is even tooling to support progressive adoption. Types that are too complex... hmmmm - I'm sure this exists in domains other than the bullshit CRUD apps I write. So yeah, I guess I don't know what I don't know here. I've written some pretty crazy types though, not sure what TypeScript is unable to represent.
- mikepurvis 2y agoProgressive code QA in general is IMO an underexplored space. Thankfully linters have now largely given way to opinionated formatters (xfmt, black, clang-format) but in the olden days I wished there was a way to check in a parallel exemptions file that could be periodically revised downward but would otherwise function as a line in the sand to at least prevent new violations from passing the check. I'd be interested in similar capabilities for higher-level tools like static analyzers and so on. The point is not to carry violations long term, but to be able to burn down the violations over time in parallel to new development work.
- nemetroid 2y agoThis is how we introduced and work with clang-tidy. We enabled everything we eventually want to have, then individually disabled currently failing checks. Every once in a while, someone fixes an item and removes it from the exclusion list. The list is currently at about half the length we started out with.
- prmph 2y ago> not sure what TypeScript is unable to represent. I want a type that represents a string of negative prime numbers alternating with palindromes, separated by commas.
- ervine 2y agoOh yeah, you have to get into branded types for this I think, which means a parsing step. Fair point.
- sesm 2y agoImportant note: TS doesn't let you enable strict mode on per-file basis. Flow allowed that.
- umvi 2y agoSometimes you don't have the type for something, especially if it's 3rd party code. For example, if you are integrating recaptcha with your page: https://developers.google.com/recaptcha/docs/loading https://developers.google.com/recaptcha/docs/loading You could try to craft your own type to match google's schema or hunt down 3rd party types, but just doing `(window as any)["__grecaptcha_cfg"]` gets the job done much faster and it's fairly isolated from the rest of the code so it doesn't matter too much.
- ervine 2y agoYeah, those are few and far between, generally there will be a DefinitelyTyped for anything popular, and you start choosing libs that are written in TypeScript over ones that aren't. But for your own handwritten application code, there is no excuse to use `any`.
- swatcoder 2y agoYou don't have to provide complete types. If you know what you need to access, and what type to expect (you darn well should!), you only have to tell TypeScript about those specific properties and values. Generally, the conveniences of allowing any are swamped by the mess it accumulates in a typical team.
- digging 2y agoAgreed; with 3rd party APIs I type down to the level of the properties I actually need. And when I use a property but don't care what the type is, I use `unknown`. That will throw an error if the type matters, in which case, you can probably figure out what's needed. Although I agree with the article that sometimes fudging the rules is acceptable, it's extremely rare that a 3rd party API is so difficult it's worthwhile. And enforcing `any` as an error means you have to be very intentional in disabling the linter rule for a particular line if that really is the best option.
- wesselbindt 2y agoWhen you quarantine third party code with an adapter (which you should probably be doing anyway), you can make your adapter well-typed. This is not hard to do, and it pays dividends.
- deleted 2y ago[deleted]
- tshaddox 2y agoFYI, TypeScript strict mode does not prevent explicit `any` (only implicit `any`). You'd need to reach for something like https://typescript-eslint.io/rules/no-explicit-any/ https://typescript-eslint.io/rules/no-explicit-any/
- ervine 2y agoFor sure, linting is just as necessary as typescript for a sane codebase.
- stouset 2y agoThe longer I’m in this industry, the more I find that there are two types of programmers: those who default to writing every program procedurally and those who default to doing so declaratively. The former like to brag about how quickly they can go from zero to a working solution. The latter brag about how their solutions have fewer bugs, need less maintenance, and are easier to refactor. I am squarely in the latter camp. I like strong and capable type systems that constrain the space so much that—like you say—the implementation is usually rote. I like DSLs that allow you to describe the solution and have the details implemented for you. I personally think it’s crazy how much of the industry tends toward the former. Yes there are some domains where going the time from zero to a working product is critical. And there are domains where the most important thing is being able to respond to wildly changing requirements. But so much more of our time and energy is spent maintaining code than writing it in the first place that upfront work like defining and relating types rapidly pays dividends. I have multiple products in production at $JOB that have survived nearly a decade without requiring active maintenance other than updating dependencies for vulnerabilities. They have had a new version deployed maybe 3-5 times in their service lives and will likely stay around for another five years to come. Being able to build something once and not having to constantly fix it is a superpower.
- ninetyninenine 2y agoIt’s not crazy, you have a million instructions and you’re just going to write all of that out in a single declaration or a list of procedures? First off the declaration is better as it’s less error prone but it comes at the cost of being harder to write and harder to interpret. Imagine if we communicated with one declarative run on sentence. No paragraphs at all… Procedures are default and easier to our nature as humans.
- dasil003 2y ago> Yes there are some domains where going from the time from zero to a working product is critical. And there are domains where the most important thing is being able to respond to wildly changing requirements I agree with your observations, but I'd suggest it's not so much about domain (though I see where you're coming from and don't disagree), but about volatility and the business lifecycle in your particular codebase. Early on in a startup you definintely need to optimize for speed of finding product-market fit. But if you are successful then you are saddled with maintenance, and when that happens you want a more constrained code base that is easier to reason about. The code base has to survive across that transition, so what do you do? Personally, I think overly restrictive approaches will kill you before you have traction. The scrappy shoot-from-the-hip startup on Rails will beat the Haskell code craftsmen 99 out of 100 times. What happens next though? If you go from 10 to 100 to 1000 engineers with the same approach, legibility and development velocity will fall off a cliff really quickly. At some point (pretty quickly) stability and maintainability become critical factors that impact speed of delivery. This is where maturity comes in—it's not about some ideal engineering approach, it's about recognition that software exists to serve a real world goal, and how you optimize that depends not only on the state of your code base but also the state of your customers and the business conditions that you are operating in. A lot of us became software engineers because we appreciate the concreteness of technical concerns and wanted to avoid the messiness of human considations and social dynamics, but ultimately those are where the value is delivered, and we can't justify our paychecks without recognizing that.
- neverartful 2y agoFor large code bases, the team has to pay the piper one way or the other. Pay up front with static typing or pay later with nearly infinite test cases to prove that it all works. To be sure, just because you're using a statically typed language does not mean that the code is bug free. It just means that it should all be correct with respect to types.
- Buttons840 2y agoThat phrase "debug working code" paints a nice picture of the unproductive part of dynamic types.
- mystified5016 2y agoThis is why I hate duck typing. I have to deconstruct the entire goddamn program to figure out what kind of object is being manipulated.