How to rewrite your JS app (at least) 10 times

76f795cabbf80024b1024517c67f0bcf?s=47 Garann Means
October 11, 2013

How to rewrite your JS app (at least) 10 times

It would be nice if developers could download a JavaScript framework, plug in the custom logic required for their particular problem, and have a functional, scalable, and reliable application. As any who's tried can probably tell you, however, it doesn't work like that. It doesn't matter if your application has 5000 "pages" or less than 50 - it will eventually become a large application in terms of code, because size of code is not just a measure of the complexity of the app itself, but of its maturity. If you've tried taking an app from a minimal set of functionality to something complex enough to cover all the edge cases, you've probably done some rewriting.
If you haven't, it's your lucky day! We'll talk about several of the ways you can almost guarantee a rewrite in your app's future. If, on the other hand, rewrites don't sound like that much fun to you, we'll also talk about how to avoid them or at least minimise the risk they present.

76f795cabbf80024b1024517c67f0bcf?s=128

Garann Means

October 11, 2013
Tweet

Transcript

  1. how to rewrite your JS app ten times* Garann Means

    ☔ @garannm * at least
  2. the hardest problem in app development

  3. worries in starting ‘ how to split things up ‘

    where does the app start? ‘ what utilities will I rely on? ‘ OMG do I have any idea what I’m doing?
  4. coding is easy. planning is hard.

  5. we tend to overprioritize the easy one.

  6. biggest worry ‘ am I going to end up rewriting

    all of this?
  7. different set of worries

  8. rewriting what already exists ‘ what’s the reason for the

    rewrite? ‘ what if I accidentally regress something? ‘ are there any yaks I can shave? ‘ OMG do I have any idea what I’m doing?
  9. ok. rewrites are inevitable.

  10. good rewrites ‘ a new technology comes along ‘ your

    business expands ‘ you upgrade your infrastructure ‘ plain old incremental refactors ‘ existing app is like EMERGENCY STATUS broken
  11. bad rewrites ‘ you’ve got 20% of the app written

    and you realize it’s all wrong
  12. bad rewrites require starting over [again].

  13. bad rewrites are not inevitable.

  14. <ul id=”bad-rewrites-i-have-done” class=”non- comprehensive”>

  15. #1 “let’s use [framework]!”

  16. we love [framework]! ‘ lots of posts about it on

    HN ‘ someone on your team used it once at a hackathon ‘ it’s MVC, you’ve already got some models!
  17. function ThisIsGoingSoWell( opts ) { this.init = function() {}; this.url

    = “cool/api/path”; } Frmwrk.extend( ThisIsGoingSoWell, Model );
  18. function ActuallyDoStuff() { // TODO: state objects? // TODO: event

    handlers? // TODO: idk this isn’t in the docs?? };
  19. ‘ solves easy problems ‘ provides common abstractions ‘ doesn’t

    help you organize your app love is fleeting
  20. the framework should be the last choice, not the first.

  21. #2 “nevermind, we’ll write our own!”

  22. it’s gonna be artisanal, y’all ‘ suited to your app

    specifically ‘ you know the code inside and out ‘ no bloat from features you don’t need
  23. ( function MyFrmwrk( window ) { this.init = function() {};

    this.url = function() {}; window.Frmwrk = this; })( window );
  24. ‘ you’re rewriting your favorite framework ‘ with just enough

    difference that other devs don’t understand it ‘ and turns out your favorite framework isn’t so easy to write congratulations!
  25. only write one application at a time.

  26. #3 “we can’t define an API without any code!”

  27. do I look like a fortune teller! ‘ don’t want

    to outline an API too early ‘ just more places to change it ‘ how can you optimize that YOU CAN’T
  28. ViewOne.render = function() { // get data, populate template, transform

    inline }; ViewTwo.render = function() { // get transformed data, populate template }; ViewThree.render = function() { // get populated template from server };
  29. ViewOne.render = function() { // get data, populate template, transform

    inline $.get( “/viewOne”, function( data ) { container.html( tmpl( data ) ); }); };
  30. ViewTwo.render = function() { // get transformed data, populate template

    $.get( “/viewTwo”, { language: “en” }, function( data ) { container.html( tmpl( data ) ); }); };
  31. ViewThree.render = function() { // get populated template from server

    $.get( “/viewThree”, { language: “en”, subtmpl: “error” }, function( html ) { container.html( html ); }); };
  32. ‘ logic that’s not in a consistent place is hard

    to find ‘ and hard to reuse ‘ and makes it hard to have a predictable API ‘ unpredictable APIs beget unpredictable architectures where the $!&#% is this getting set?!
  33. specify APIs thoroughly and early.

  34. #4 “good point, if it’s not JS we’ll do it

    in PHP”
  35. super-ultra-optimized ‘ static stuff rendered by server ‘ dynamic stuff

    by client ‘ server folks speak their language, JS folks speak theirs
  36. oh wait. ‘ this partial template right here? ‘ we

    need it on the client, too ‘ no big, just copy it in JS ‘ (six months pass) ‘ arrrrgh why didn’t we just write these templates in the same language
  37. plan the stuff outside your JavaScript as well.

  38. #5 “this DOM doesn’t work with our webapp”

  39. Polly-who? ‘ there are a lot of inconsistencies (still) ‘

    that’s a lot of extra code ‘ standard implementations may not be quite right ‘ let’s just write our own DOM!
  40. // _so classic_ ( function FileUploader() { // we’ll just

    make a little file selector // and style it real pretty // and add some cool progress events // and the Flash movie that runs it all })();
  41. // oh. ( function FileUploadHandler() { // expose all that

    Flash stuff to JS // let the widget be used in a larger form })();
  42. // srsly? ( function FileUploaderDegrade() { // fall back to

    normal upload if no Flash // check for XHR2, add progress stuff // map DOM API so it matches FileUploader // let the widget be used in a larger form })();
  43. ‘ someone at [browser co, inc] is working on improving

    this stuff ‘ someone else made a polyfill ‘ someone else fancied it up and made a jQuery plugin ‘ if you’re not in the widget business, stop we used to have to do this; we don’t now
  44. only write one application at a time.

  45. #6 “XHR? more like HTML amirite”

  46. we’re fetching HTML anyway ‘ let’s put all our stuff

    in it! ‘ object setting? data attribute ‘ i18n? data attribute ‘ entire app state??? DATA ATTRIBUTES
  47. MyApp.init = function() { // look, no options! // we’ll

    just select the necessary nodes.. // and then find their attributes.. // parse those to data types.. };
  48. MyApp.onSomethingChanged = function() { // we’ll just select the necessary

    nodes.. // and then set their attributes.. // and update the server, too.. // wat, 503?! // we’ll just select the necessary nodes.. // and then unset their attributes.. };
  49. ‘ it’s expensive and messy ‘ it violates DRY from

    the jump ‘ it doesn’t provide objects you can work with the DOM is not a data store
  50. data deserves a real home.

  51. #7 “there’s a plugin/module for that!”

  52. the wheel is perfect ‘ and there are so many

    wheels to choose from! ‘ a tried and true solution for everything ‘ partway there still beats the starting line
  53. function veryImportant( item ) { var $item = $( item

    ); $item.transformIntoMagicWidget(); // NOW we can actually use it $item.on( “pluginEvent”, superImportantFn ); }
  54. function superImportantFn( e ) { var $t = $( this

    ), important = $t.pluginVal(); $t.addClass( “specialState” ); $t.removeClass( “pluginState” ); $t.find( “.aChild” ).append( coolStuff ); $t.find( “.someBtn” ).prop( “disabled” ); // $t.find( “ugh this is a nightmare” ); }
  55. /* Name: MagicWidget™ Author: not you! License: lol wat */

    $.fn.transformIntoMagicWidget = function() { return this.each( function() { // MODIFIED: grm 11-10-2013 // added all that state and CSS stuff ... }); };
  56. ‘ you’re writing a whole new plugin ‘ based on

    something you don’t control ‘ that might get abandoned ‘ and getting minimal value from the original oh hey.
  57. don’t use external tools that almost do something.

  58. #8 “MVC means never having to say require”

  59. but this framework doesn’t use AMD ‘ single point of

    entry = one place to manage dependencies ‘ it’s already clear what the code is doing ‘ loading stuff is abstracted out anyway ‘ the internet says you don’t have to worry about speed if you don’t have any JPGs
  60. BigController = function() { BigView.renderEverything(); };

  61. ► Uncaught Error: Object #<Object> has no method ‘renderEverything’

  62. BigController = function( refresh ) { if ( refresh )

    { BigView.setData( data ); } BigView.renderEverything(); };
  63. ► Uncaught ReferenceError: data is not defined

  64. BigController = function( refresh ) { if ( refresh )

    { if ( data ) { BigView.setData( data ); } else { $.get( “/newData”, function( d ) { BigView.setData( d ); }); } } BigView.renderEverything(); };
  65. ► Uncaught Error: u should use a dependency loader

  66. ‘ relying on globals or sub-globals is fragile ‘ lots

    of tests to see whether things are available ‘ whole chain has to be reexamined for each edge case ‘ can’t abstract out loading lack of modularity encourages messes
  67. assume everything will be a module.

  68. #9 “it works with [terrible thing you use now]!”

  69. we’re limiting our risk! ‘ less to rewrite ‘ more

    time to improve ‘ makes use of all that hard work we did on the original
  70. /* TODO: delete me i am being replaced function doStuff()

    { var output = TerribleThing.use(); Module.transform( output ); OtherModule.render( output ); CoolModule.observe( output ); } */
  71. function doStuff() { var output = TerribleThing.use(); Frmwrk.transform( output );

    Frmwrk.TrriblView.render( output ); Frmwrk.Events.observe( output ); }
  72. ‘ rewriting around existing code tends to give you.. the

    same code ‘ if a tool is bad or niche, no framework on earth can save you from it ‘ trying to avoid change limits your ability to improve ‘ which leaves you itching to.. rewrite you saved 4 LoC!
  73. throw out assumptions about how things will work.

  74. #10 “we found our solution; what’s the problem?”

  75. this thing looks great! ‘ super modern ‘ fast benchmarks

    ‘ incredibly flexible, rich tooling, great community, thorough docs, brilliant API, scales to infinity, tiny download, hipster syntax, cool colors, foot massages, free unicorns, and a trip to space!
  76. but does it solve your problem? ‘ easy to gloss

    over if you haven’t defined your problem ‘ no i mean like broken it down ‘ no into more than three bullet points ‘ taking the time to define problems avoids irrelevant solutions
  77. to proof of concept you have to know what to

    prove.
  78. </ul>

  79. no more. we don’t want any of that.

  80. none of those are uncommon.

  81. it’s hard to avoid rewriting completely..

  82. the goal is not to do it more than once.

  83. rewrite planning ‗ can you devote the people? ‗ do

    you know the problems the rewrite should solve? ‗ do you have tests to avoid regressions? ‗ most important: can you commit?
  84. #0 way to rewrite a lot: cold feet.

  85. you have to commit ‗ don’t expect it to happen

    overnight ‗ don’t expect to iterate on atomic pieces ‗ don’t fail fast (you did that your current app) ‗ don’t start coding until you have a plan
  86. throw out everything you know about RAD.

  87. a plan you can’t commit to isn’t one.

  88. good plans keep you from getting to ten rewrites.

  89. thanks! @garannm ☔ garann@gmail.com ☔ garann.com