2

I like typescript, and I want to add it to some projects in the company.

Thing is, it is rather powerful, probably too powerful for it's own good at times.

From experience, it doesn't take long until someone who knows the type system just enough to be dangerous to define types and interfaces that are as complicated as a small program by itself.

This can be enforced culturally, to an extent, but then what has happened previously is that whenever I see a tool that could be dangerous has been added, there often comes a point where people utilize these dangerous points and the code base ends up being less-maintainable.

Basically I consider type systems to be very good as long as they define what a list is, what a list contains, what variable is a string and what variable is a number of what kind and so on, but once sufficiently advanced types are introduced, these can pretty much do everything with the right lines -- and worse, they do. It no longer provides much of a safety and even worse, isn't easy to read or maintain down the line.

I'm wondering if there's any technical way of enforcing this, a linter, some other tooling, that would prevent people from defining types too complicated for their own good?

I know about eslint typescript but I haven't noticed any rules that demote a defition just because of its complexity.

4
  • 1
    Could you give some examples of code you think is "too complicated" and "dangerous"? While to some extent this is a team/company decision, if your personal preference is significantly different from the "normal" use of Typescript, you're going to find that this could be an uphill battle. Dec 31, 2022 at 11:43
  • 2
    I'm confused. Is your alternative to have the same complexity, just in an untyped mess of JS? Or does JS have a tool to prevent this that TS does not have?
    – nvoigt
    Dec 31, 2022 at 11:44
  • 4
    As tech lead, statements like "for their own good?" Must be avoided at all cost if you don't want developers getting frustrated and leaving by the incapacity to decide (by their own) what's good or bad. You can give advices and enforce some guidelines but you should not limit their options when it comes to code. Coding is (still) a very very personal thing. If you treat others like kids, that's what you will get.
    – Laiv
    Dec 31, 2022 at 11:47
  • I fully agree with OP that the quest for perfect type safety makes some very intelligent TypeScript developers define complex types that almost nobody understands. The debugger can step through the code, but not through 5 lines of type definition. Thanks for the question and thanks for the answers! Dec 12, 2023 at 19:24

3 Answers 3

5

You cannot eat the cake and have it.

When you switch from a less feature-rich programming language (or language version) to a more feature-rich one, you will have to accept the fact that your developers start to use those extra features actively. And if you have developers which are used to program in a Fortran style, be sure, they will find a way to write Fortran in any language. Note your question is in no way specific to Typescript, it applies to a lot mainstream programming languages where every year and then new powerful features are introduced.

Moreover, the line between "acceptable" constructs and "too complicated" is blurry, highly opinionated and will shift over time with the experience a developer gains.

So what can you do? Well, I think you are already on the right track, since you wrote

This can be enforced culturally, to an extent

And that is pretty much all you can do - but you can do this very consequently.

For example, make sure you have a pull-request based workflow with code reviews for each change to the code base. Make sure your code review checklist does not only contain braindead rules like indentation and case conventions, but also points like "is the code snippet / function / type overly complicated", or "does this variable / function / type form a proper and useful abstraction which helps to make the code more readable / reusable / testable?"

Other "tools" could be regular workshops and trainings for your devs, giving them easy access to books like "The Pragmatic Programmer", and encouraging them to improve not only to increase their knowledge about new language features, but also their knowledge about how to write simpler, more readable code.

This finally leads to giving your team a chance to work out the lines between "good usage" and "bad usage" of certain language features by themselves.

4

It might help to stop referring to these features as having "power", but instead as having "confusingness", as "lacking obviousness", as being "not closely matched to any particular purpose", and so on.

My point is that if you stop implicitly flattering people for possessing a supposed mastery in the use of these features, and instead start talking about how they demonstrate a weak and over-complicated style - typical of an unguided apprentice and a toolbox they don't know how to use - then the culture might be half the way there to controlling "dangerous" use of features.

The other side of this coin, however, is having masters present who understand how to get things done by simple and effective means. There have to be accepted approaches which have already been demonstrated.

It's often not clear what is simple and what is over-complicated, until you have all the different examples in front of you. People can't be expected to avoid mistakes which they haven't seen or even conceived in their imaginations.

There is of course no machine or mechanical process that is capable of assessing the complexity and readability of code in general. A search for such a machine is, itself, a kind of apprentice-thought.

Every six months, some new and naive software manager gets the idea that there must be some kind of machine capable of measuring things about code like correctness (i.e. the application's fitness for its purpose), human readability, conceptual integrity, and the relationship between the complexity of the code and the essential complexity of the purpose it has to serve. Ha, ha, ha, bless their souls!

The real answer here is either to stick to an established language where everyone already knows all the accepted techniques, or else accept that moving to a new language is going to involve a period of adjustment where there will have to be re-establishment of accepted techniques, and in the meantime there may be waste, rework, or degraded output, whilst everyone gets used to new ways.

If Typescript is a superset of some language you already use, then part of managing the transition might be to enumerate the new features, and declare that any use of them must be collectively reviewed (either after implementation but before release, or before implementation if there risks being a lot of rework due to bad use). But it won't completely avoid mistakes that only become apparent after they are already made and can be digested.

3

This is primarily a social problem and should therefore be tackled on a social level.

It may be helpful to compare overly complex types with overly detailed tests. Both help to build your confidence that the software system is working as expected, but both have diminishing returns. At some point, the costs of adding more tests or creating more accurate type checks doesn't outweigh the benefit of spending that time on creating value some other way.

So, the issue isn't necessarily type checking, but prioritizing work, and creating code that is accessible to all team members.

I don't know how your team is organized. But even if everyone works in a fully self-directed manner, it's useful to have check-ins where people explain what they did and what they will be tackling next.

This also gives opportunity for people to point out higher-impact opportunities. E.g. instead of type-checking and testing old code that already works well, creating a new widget might be more valuable. On the other hand, this can be an opportunity to convince the rest of the team: if we add expressive type checking to this API, we can eliminate an entire class of bugs that has plagued us. All in all: talking with each other helps to build shared values about the project.

Whether code is overly complex is a subjective judgement, taking into account the skill distribution of the team. While linter rules can help, this ultimately comes down to human code review. If the team doesn't have a culture of requesting and giving feedback where appropriate, that might be a bigger issue than complex code in itself.

When dealing with complex code that has value, also consider that there are multiple ways to react:

  • You can upskill the team so that everyone feels comfortable with the complexity – expensive now but potential long-term benefits.
  • You can abstract over the complexity, so that the complex code csn be used without needing to understand its internals – in many ways the standard approach for dealing with complex software systems, providing the value of the complicated part, but without actually eliminating the complexity.
  • You can simplify the code – ensuring maintainability but giving up the value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.