42

I've recently poured a couple of hours into JavaScript because I wanted to benefit from the massive userbase. Doing that I have noticed a pattern that most people attribute to dynamic languages. You get things working really quickly, but once your code reaches a certain size you waste much time with type, spelling and refactoring errors in general. Errors a compiler would normally spare me from. And not have me looking for errors in the logic when I just made typo in another module.

Considering the incredible following JavaScript and other dynamically typed languages have I am lead to believe that there's something wrong with my approach. Or is this just the price you have to pay?

To put it more concisely:

  • How do you approach a JavaScript (or any other dynamic language for that matter) project with ~2000 LOC?
  • Are there tools to prevent me from making those mistakes? I have tried flow by Facebook and JSHint which somewhat help, but don't catch typos.
  • 2
    Even though there are ways to mitigate the costs, there are costs. – dcastro May 9 '16 at 13:51
  • 29
    I'd try writing your program in a statically typed language that compiles to javascript, like Typescript, Scala.js or Elm. – dcastro May 9 '16 at 13:52
  • 6
    testing, testing, more testing, and coverage reports. – njzk2 May 9 '16 at 18:33
  • 7
    ~2000 LOC is a small project. It should easily fit into what a dynamic language does easily and well. If you're struggling with that kind of size project you have a more fundamental issue with your programming skills than anything that is relevant to dynamic languages specifically. – Jack Aidley May 9 '16 at 21:24
  • 5
    @JackAidley Disagreed. OP is used to focus on high-level problems and not whether an identifier is spelled properly. That is programming skill. Ensuring correct spelling can be done by a middle grader and/or tool support. – mucaho May 9 '16 at 21:48
37

Specifically speaking of JavaScript, you could use TypeScript instead. It offers some of the things you are referring to. Quoting the website:

Types enable JavaScript developers to use highly-productive development tools and practices like static checking and code refactoring when developing JavaScript applications.

And it is just a superset of JS, meaning some of your existing code will work with TS just fine:

TypeScript starts from the same syntax and semantics that millions of JavaScript developers know today. Use existing JavaScript code, incorporate popular JavaScript libraries, and call TypeScript code from JavaScript.

  • 11
    ... and TypeScript is essentially Ecmascript 6. – Robert Harvey May 9 '16 at 14:26
  • 11
    Uh, that quote hurts. It just shows that Microsoft has always been a static languages company that simply doesn't understand dynamic languages. "Types enable … code refactoring"? Really? Does Microsoft's PR department realize that code refactoring as a practice was invented in Smalltalk (a dynamic language) and existed even before that in Forth (a typeless language)? That the very first automated refactoring tool was part of a Smalltalk IDE, before static languages even had IDEs? That modern Smalltalk IDEs have refactoring tools at least as powerful if not more as Java, C#, and C++? C'mon. – Jörg W Mittag May 9 '16 at 16:09
  • 5
    TypeScript is a great language on its own, why do you have to try and push it with such nonsense? – Jörg W Mittag May 9 '16 at 16:09
  • 29
    @Jörg I don't know enough about Smalltalk, but every single IDE for JavaScript or Python I have seen is miles behind what a good IDE for Java or C# can do. Also there are some things that are just plain impossible to do in a dynamic language (some of the most popular refactorings really): Say you have a public function foo(x) { return x.bar;} or anything like that. Since there is no type information and the function is public (hence you can't know all the callers) it is impossible for you to figure out whether bar should be renamed to baz if you rename some class. – Voo May 9 '16 at 17:01
  • 10
    This answer says that the solution to "dynamic language mistakes" is not to use a dynamic language at all. – bgusach May 10 '16 at 8:53
19

There are some approaches which can help:

Unit testing

Write unit tests where possible. Solely relying on manual testing or finding bugs in the wild is hit-and-miss.

Use frameworks

Rather than rolling your own and risking the introduction of bugs, use established frameworks where possible.

Prefer CSS/high-level languages

Where you can cede functionality to CSS or whatever high-level language you're writing in.

Refactor

Refactor to reduce the amount of code. Less code = less places for things to go wrong.

Reuse

Reuse existing code where you can. Even if code isn't an exact match, it can be better to copy, paste and modify rather than writing something afresh.

IDEs

Modern IDEs generally have at least some Javascript support. Some text editors are also Javascript aware.

  • 5
    While true, your advice applies basically to every programming language and mainly aims at fixing logical mistakes rather than those that arise from dynamic languages. – edmz May 9 '16 at 17:40
  • 1
    "your advice applies basically to every programming language". Very true - in a similar way to going thru the gears from hobby projects to full fat enterprise solutions, which requires an increasing amount of strictures, similarly - the more Javascript that is written, the more discipline it needs if the wheels aren't going to rapidly come off. Eric Lippert describes this very well. – Robbie Dee May 9 '16 at 17:48
  • 4
    "Prefer CSS/high-level languages" — I don't really understand what this bit means in relation to JavaScript: are you saying to move elements (like animation, perhaps?) into stylesheets rather than JS code? How does CSS relate to high-level languages? – anotherdave May 9 '16 at 18:18
  • @anotherdave A lot of what used to be firmly the domain of Javascript can now be achieved in CSS3. Some functionality could also possibly be moved to a higher level language which would be subject to more stringent controls. – Robbie Dee May 9 '16 at 20:15
  • 4
    @anotherdave Much of what people try to do with JavaScript is extraneous and inappropriate. Libraries that provide standard language tools, frameworks that provide standard HTML elements with little more, code that replicates basic functionality like anchors, MVC emulation, styling, DOM reimplementation, AJAX abstraction, rendering trivial objects (reimplementing SVG), polyfilling features that don't benefit the user… You should minimize the amount JS that you write. If you can do it without JS, do it without JS. – bjb568 May 10 '16 at 3:36
2

One tool that hasn't been yet mentioned is simple, file-local or project-wide text search.

It sounds simple, but when you include some regular expressions you can do some basic to advanced filtering, e.g. search for words located in documentation or source code.

It has been an effective tool for me (besides static analyzers), and given your project size of 2k LOC, which isn't particularly large in my opinion, should hopefully work wonders.

  • 2
    grep goes a long way. Unless you don't do too weird dynamic things, it does the trick. However, it feels very manual if you are used to IDEs for static typed languages. – bgusach May 10 '16 at 8:52
1

I am currently refactoring several thousand lines of code on a large AngularJS project. One of the biggest hassles is to figure out the exact contract of a given function. I sometimes ended up reading API documentation because elements of the raw API response were assigned to variables that went through 6 layers of code before being modified and returned through 6 more layers of code.

My first advice is to design by contract. Take specific input, produce specific output, avoid side effects, and document those expectations using TypeScript or at least JSDoc.

My second advice is to implement as many checks as possible. We follow the AirBnB standard and use eslint on our entire code base. Commit hooks verify that we always follow the standard. We naturally have a battery of unit and acceptance tests, and all commits must be reviewed by a peer.

Switching from a text editor (Sublime Text) to a proper IDE (WebStorm) also made it much easier to work with code in general. WebStorm will use JSDoc to give hints about expected parameter types and raise error if you supply the wrong type or use the a return value in the wrong way.

In JavaScript, new features such as symbols and getter/setters can help enforce a certain level of quality by adding assertions to variable assignment (e.g. make sure the integer is within range, or that the data object has certain attributes).

Unfortunately, I don't think there's a true solution to prevent dynamic language mistakes, only a series of measures that can help reduce their frequency.

0

My answer to the question “How do you approach a JavaScript (or any other dynamic language for that matter) project with ~2000 LOC?”

I develop PDF form applications. I approach my JavaScript software development project (regardless of source code size) using Petri’s net elements and annotations. The method is not tied to any particular programming language technology. Thus it may be used for other “programming languages”.

I create a diagram of the application logic. To keep the diagram uncluttered I add most of my annotations to a form that I use with the diagram. The entries in the form include references to properties or functions. Then I write out the source code based on the information in the diagram and entries in the form. The method is systematic because every source code written is directly mapped from the diagram and entries in the form. The source code can be easily checked because I also follow naming and coding conventions when I write the code.

For example, I have chosen a convention that all functions are prototypes. If performance become an issue then it can be improved by declaring the functions in the constructor. For some properties I use arrays. Again if performance becomes an issue then it can be improved by using direct references.

I also use eval. This can greatly reduce the size of source code. Because of performance issues, I use eval at the beginning or initialization part of my application; I never use it in the “runtime logic” – this is another coding convention that I follow.

protected by gnat Jul 24 '16 at 23:19

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.