5 ms·
I spent 10 hours yesterday refactoring and adding features to a client side enterprise application that is well over 20k lines of TypeScript over tons of files.
by pixie_ 10y ago
I spent 10 hours yesterday refactoring and adding features to a client side enterprise application that is well over 20k lines of TypeScript over tons of files.
Yesterday was a pretty productive day for me. TypeScript easily caught hundreds of little errors as I coded and refactored which I fixed just a quickly. Most of the time when I tested my changes things just worked. I thought to myself every time TypeScript found something, 'wow I just avoiding another runtime error, thanks'
Maybe people don't write or maintain client side software at scale. I don't know. But there's so little cost to having static typing I don't know why you wouldn't want it. I am easily an order of magnitude more productive with it. I code faster, my code works first time around more often, other people can understand my code easier, and my tests don't have to worry about validating types so they can be more logic oriented.
There is zero chance anyone would of been able to be as productive as I was yesterday if that code base was dynamically typed. Not even close.
- d357r0y3r 10y agoThis is something I hear time and time again once a person has actually learned the technology (in this case, TypeScript) and seen the benefits in practice. For many pure front-end devs that have never really worked with static typing, the compiler just seems like another arbitrary build step in the pipeline with no discernible benefit.
- Someone 10y ago"…a client side enterprise application that is well over 20k lines of TypeScript over tons of files […] But there's so little cost to having static typing I don't know why you wouldn't want it" I don't think the OP would claim TypeScript is a bad idea, as, with its relatively weak type system, it doesn't really suffer from "if you find a bug it’s easy and unintrusive to add a test for it, but may require a substantial amount of work to refactor your code to add types that make the bug impossible". As an example of a strong type system, in Haskell code, the database access layer will may types such as "ISO-8859-1 string of at most ten characters" If it turns out that you want to store ISO-8859-2, changing that type can and often will cause a cascade of necessary changes, for example because you're calling a ISO-8859-1 to UTf-8 conversion function, and the compiler will not accept that call to now take a ISO-8859-2 string, or because you are concatenating that string with another ISO-8859-1 one. Then, you will have to decide whether to convert locally or whether to also change the type of that second string, etc. Now, of course, each of those compilation errors becomes a bug if you just use variable-sized byte arrays. Question is how much work you are willing to do to prevent those bugs.
- cle 10y agoExactly. As with everything, there is a tradeoff, and the general answer is always "it depends". Or if you have to choose between one or the other, the answer should be "yes". Static typing provides universal guarantees, which sounds great at first, until you realize that it really does mean "universal". Sometimes that extreme rigidity makes changes very difficult, especially when you're dealing with cross-cutting changes across many teams and organizations. I work in a very large company with lots of services. The best practice, espoused by most of the senior/principal engineers (particularly ones with extensive SOA experience), is to keep your service interfaces and document models dynamically-typed. It makes them much easier to change. Big systems should be assembled from loosely-coupled components which are internally rigid but externally flexible and dynamic.
- MasonOfWords 10y agoService interfaces should be well-typed. Almost by definition, the cost to update a service interface will be a fraction of the time associated with updating its consumers and testing their integration. Dynamic types can lower a part of the cost (although not by much, in comparison to modern languages and frameworks), but add more expense in many other ways. When operating at any sort of scale, a system that's "easy to change" stops sounding virtuous. Communicating and integrating service changes effectively is usually more expensive than making the changes themselves, for a large organization. Providing service descriptions which can be converted into static types (e.g. swagger) is very cheap, and can prevent a host of expensive human errors. In truth, poorly specified services or documents can end up impossible to change, since the risk and impact of doing so is difficult to size (or the exercise of doing so costs more than the change is worth).
- cle 10y agoA system that's easy to change is very important at scale, unless you want to write a new API or service every time a new business requirement arises. In many situations, a true statically-typed service interface can be nearly impossible to change, or even add fields to. Adding new schema versions (particularly for write APIs) or dynamic/polymorphic schemas that change based on the input are extraordinarily useful and very easy with dynamically-typed interfaces, but can be very difficult with strict statically-typed interfaces, particularly the kind that e.g. Java nudges developers towards. There are some hybrid frameworks that mix dynamic and static typing, which solve some of these problems (e.g. protobuf, Avro, Ion). They make reasonable compromises.
- yogthos 10y ago>There is zero chance anyone would of been able to be as productive as I was yesterday if that code base was dynamically typed. Not even close. My team did a big refactor on our ClojureScript code base last week. We've been developing the project for over a year now. We had no trouble with type refactoring. We use Schema (https://github.com/plumatic/schema https://github.com/plumatic/schema) to validate the data at the edges, and the REPL to run code as we write it. Type errors simply have never been a problem for us.
- pixie_ 10y agoAre these cases easy in ClojureScript - I change the property 'Part' to 'PartList' on my DTO and now I need to find all places that Part is referenced and update them. I have a function that takes a 'User' as a parameter, but I don't know what the property is called for the display name. Is there an easy way of figuring it out without running the app? REPL wouldn't help because I dont even know how to create a User object (this is a very large code base that I didn't write myself) I add a new parameter to a render() function and I need to find out all the places the function is called so I make sure to update them. I make a spelling mistake calling a function or referencing a property. Do I get any instant alert to the error or do I have to run the app, or run REPL on it? If I'm changing tons of code across many files I don't want to have to run/REPL for every little thing to validate that I didn't make a dumb mistake. Ideally the mistake is caught as soon as I type it. Does ClojureScript have this?
- yogthos 10y ago>I change the property 'Part' to 'PartList' on my DTO and now I need to find all places that Part is referenced and update them. Yup, I use Cursive IDE (https://cursive-ide.com/ https://cursive-ide.com/) and it provides precise symbol lookup using static analysis. >I have a function that takes a 'User' as a parameter, but I don't know what the property is called for the display name. Clojure is a functional language so I don't have a problem of dealing with a plethora of classes. The data model is defined using the schema and I can easily tell what it looks like. Furthermore, since it's a functional language, I don't have any global state to worry about. I can try any function in the REPL in isolation to see what it's doing and how it behaves with different inputs. Destructuring (http://clojure.org/guides/destructuring http://clojure.org/guides/destructuring) is alos commonly used for readability in Clojure. When a function takes a non-primitive type, it's idiomatic to destructure it to show what the shape of the data is right in the arguments. >I add a new parameter to a render() function and I need to find out all the places the function is called so I make sure to update them. Yup, absolutely. I can look up usages of a function in Cursive. >I make a spelling mistake calling a function or referencing a property. Do I get any instant alert to the error or do I have to run the app, or run REPL on it? Yes, Cursive will highlight undefined symbols as well is invalid function arity. I get immediate feedback if I mistype something, try to use a function that doesn't exist, or pass wrong number of arguments to a function. >Ideally the mistake is caught as soon as I type it. Does ClojureScript have this? That's generally exactly what happens in my experience, and in addition I also have the REPL that lets me run any code if I'm not sure about what it's doing.
- notacoward 10y agoRefactoring's a different ballgame, where the cost of static typing has already been paid and the benefit is larger (primarily because the risk of type errors otherwise is much higher than with new code). Since most code spends far more of its lifecycle being modified than being written in the first place, that makes the case for static typing much stronger, but I still share the OP's skepticism about static-typing advocates' claims.
- sametmax 10y agoWell, the real cool thing about typescript or python's type hints is their optional nature. It means you can get the best of both worlds: the quick script and the big project, the pro and the beginner, etc.