Skip to content

Instantly share code, notes, and snippets.

@buwilliams
Last active June 7, 2021 13:44
Show Gist options
  • Save buwilliams/5f0d64dd02b307181c5b2ead4f923148 to your computer and use it in GitHub Desktop.
Save buwilliams/5f0d64dd02b307181c5b2ead4f923148 to your computer and use it in GitHub Desktop.
Questioning TypeScript

My guiding value of frontend development: Changability

TLDR: TypeScript vs JavaScript is a programming war. A war over dynamic typing vs static typing. My view is that TypeScript introduces too many concepts to be worth it's benefits.

Exploring a nagging feeling

Feelings, in my view, are signals, they exist to communicate complex thoughts. Often, I've found myself saying, that doesn't feel right. Yet, that kind of communication doesn't teach or shed light. It merely shows where you lean. Years ago, I was challenged to articulate such feelings into clear explanations so that everyone else could benefit.

Our team has been discussing improvements to our frontend. I suggested that TypeScript is, to me, is not a desirable solution. I wouldn't use it in my own projects nor would I encourage the use of it. This article is an attempt to explain why I feel this way.

What users want from our products:

  • Solve real pains and/or grants desires
  • Beautiful design
  • Usable interfaces (don't make me think)
  • Dependable outcomes
  • Performance
  • Privacy & security

For this article, I am chiefly concerned with performance.

Two stories of performance

I'm concerned with two stories: users & developers.

  • The User, or the speed of getting value

    • Page load
    • Loading data
    • Button clicks
    • Navigating
  • The Developer, or the speed of creating value

    • Adding new features
    • Changing existing features
    • Repairing broken parts
    • Ensuring quality

What slows down a user?

Here's a short list:

  • Large page download sizes
  • Complex DOM and Layout thrashing
  • Slow server-side response times
  • Amount of logic which must be processed to render layout

There is a relationship between download footprint and application complexity. Additionally, as complexity rises, so does performance fall. Any technology which increases the footprint to our page contributes to performance degradation. Therefore, the larger the memory footprint, the slower the application will be.

What slows down a developer?

Here's a short list:

  1. Concepts encountered (WTFs per minute)
  2. Intertwined (or complected) code
  3. Rigidity caused by coded structural assumptions, aka. guard rails
  4. Build times

There is another relationship between number of programming concepts and speed of introducing change. The larger the number of concepts (or abstractions), the slower it will become to introduce change into the system.

I agree, we should optimize for code readability. TypeScript introduces so many concepts, how does that help?

Trying to convince Posthog developer's that:

1. The frontend is change city. Therefore, optimize for changeability.

  • The frontend is the most likely part of a web app to change frequently and radically.
    • Navigation systems rewritten
    • Entire features replaced in favor of new designs
    • Massive data structure changes to support new designs
    • The list goes on... the frontend in the messiest part of the stack.
  • Therefore, it needs to be easy to understand and accommodate change naturally.

2. TypeScript doesn't prevent bugs, it prevents one kind of bug.

  • JavaScript is simple and expressive, contributing to its popularity.
    • Its expressiveness is exactly what scares developers because of net negative producing programmers, NNPPs for short.
  • TypeScript introduces guard rails to make us feel safe while not actually delivering on hopes to stop difficult bugs.
  • This "safety" increases the number of concepts drastically. Which means:
    • Large changes require senior people to address them. We are already seeing this.
    • Bugs become much harder to diagnose.
    • On-boarding new programmers is hard.
  • TypeScript will only ever catch type errors. It will not catch logic or algorithm errors.

3. It's better to trivialize bugs than prevent them.

Below is a contrived story of a type system and how this solution increases complexity rather than reducing it. The Programmer represents quality code and NNPP bad code. These do not need to be different people, merely, the same person who didn't get enough sleep last night.

// Plain JavaScript
function sum(value) {
    return value.x1 + value.x2;
}

// NNPP's code
sum(1); // -> NaN

// This is a small example of passing in the wrong type.
// A programmer sees this method and thinks:
// 1. Oh man, they can give me anything! This is scary!
// 2. I better find a way to prevent this.

// Programmer's attempt to solve the problem
// in plain JavaScript

function sum(value) {
    if(typeof value !== "object")
        return new Error("value needs to be an object");
    if(!value.hasOwnProperty("x1"))
        return new Error("value needs to contain x1 property")
    if(!value.hasOwnProperty("x2"))
        return new Error("value needs to contain x2 property")
    if(typeof value.x1 !== "number")
        return new Error("x1 property should be a number")
    if(typeof value.x2 !== "number")
        return new Error("x2 property should be a number")
    
    return value.x1 + value.x2;
}

// NNPP's code
sum(1); // -> value needs to be an object

// Programmer:
// 1. Shoot! We'll still get an error at runtime.
// 2. How can we catch these at compile time?
// 3. Oh, I've got it: Type Checking!

// Not real JS, used to make an example

type Coordinates = {
    x1: Number,
    x2: Number
}

function sum(value: Coordinates): Number {
    return value.x1 + value.x2;
}

sum(1); // -> compile time error: invalid type: number

// NNPP: Shoot! My code isn't compiling!

let myValue = new Coordinates(1, 2);
sum(myValue);

// NNPP: Yay, it works!

What have we gained with the new type system?

  1. Type mistakes will be caught before runtime.
  2. Less type checking code in our methods.

What are the downsides?

  • We've increased the number of concepts required to understand code and not by a small amount.
  • Slower build times.
  • Compatibility issues since you'll need to jump in and out of the type-system.
  • We are required to cover cases that will not occur but are from a code perspective, possible.
  • The generated code from the type-system will have a larger footprint.
  • and more...

Would it have been better to fix the NNPP's function invocation?

sum(1)
// becomes
sum({x1: 1, x2: 2})

Plain-old JavaScript is easy to understand and debug at both compile time and runtime.

Techniques for preventing bad type code

  • Encourage simple, even boring code, in fact, the more boring the better.
    • YAGNI, you ain't gonna need it
  • Encourage encapsulation in the form of modules.
  • Discourage arbitrary OOP structures which introduce new concepts
  • Prefer flat data structures
  • SLAP methods (single-level of abstraction principle)
  • JAMStack architecture, reduce and limit the amount of code on a simple page

TypeScript is not simple

If you believe programmers cannot be trusted with simple method invocations then do you really believe giving them a type-system will make it better? They already couldn't handle a method signature, what happens when they get an inherited interface? We've seen this play out in Java. Why are we not learning from past mistakes? There has to be a better way forward.

A short glance at TypeScript Handbook's The Basics shows the following menu:

  • Static type-checking
  • Non-exception Failures
  • Types for Tooling
  • tsc, the TypeScript compiler
  • Emitting with Errors
  • Explicit Types
  • Erased Types
  • Downleveling
  • Strictness
  • noImplicitAny
  • strictNullChecks

That's the basics...

With Frontend + TypeScript, we have a system that will change frequently getting inundated with large, and I dare say, needless complexity and to what end?

The old programming war: dynamic vs static typing

The rise in popularity of JavaScript, Node, Ruby, and Python in it's early days is partially attributed to dynamic typing. Statically type vs dynamically typed languages is a good article comparing the benefits of each.

It's a preference. Some developer prefer type safety while others do not. It's by no means a settled argument. In my case, catching type errors are not worth the weight of a type system. Other developers see type errors as their number one complaint. I think this difference can mostly be attributed to conditioning.

With static types you get:

  • Code completion
  • Code navigation

If you live in the world of static typing, you'd quickly miss these things. It would also feel unsafe.

So, it's an unsolved programming war between preferences. In such matters, it good to share you opinion and leave it at that.

A counter argument: Code Completion

One of the most surprising finds is the difference an IDE will make. Turns out this debate might be solved by asking which editor a person prefers. If you have code completion, so much of the pain of typed languages becomes trivialized because the editor is doing all the hard work for you. Who can deny that code assist is amazing?!

If, however, you prefer to write all your code by hand, you'll likely hate types. You have no code assist. You have no intelligent code completion. All you have is loads more syntax to write.

So, it follows, that if you try to do TypeScript without code completion, you'll dislike it. Some people prefer light weight editors and writing code by typing it all out. It explains the popularity of Atom and Vscode.

Therefore, if my editor does the hard work of manging syntax, giving useful feedback, and auto-completing lines of code then who could deny that type information is powerful? In manual editors, this is a vast chore.

Well if we’re not writing code on paper, then why stop half way?

Summary

I may not be able to convince you that TypeScript is not worth the burden. Differences in opinion are a boon to us all. I do hope to show that we should optimize the frontend for change. I remain open-minded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment