Fronteers — vakvereniging voor front-end developers

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


Thank you.

Yeah, so I'm going to talk to you today about how to-- how to rewrite your JavaScript application or if that's not something that you're wanting to do, 10 times at least, then maybe how to avoid that.

So I'm going to start with a premise that I hope you guys will agree with me on, which is the hardest problem in application development, that being this dude right here, when you start your application development, the blank Editor window that stares you in the face and makes you question yourself as a human being and a developer and kind of throws you into some worries, because there are a lot of things to think about when you begin developing an application.

Obviously, you want to think about how you're going to divide things up, what's going to go where, and how is it all going to work together.

You need to think about the flow of the application, like, where does it-- where does it begin and who's in charge of making sure that it begins and what do you need to pass in order to make it begin? You want to think about the tools that you're going to be using.

And if you're doing something somewhat niche, you might end up using third-party tools that you actually rely on very heavily, things that aren't just nice to haves but are actually crucial to your application.

And then, obviously, you wonder, like, whether you really know what you're doing.

So the idea is that it's actually fairly easy to write code.

It's easy to write a for-loop.

It's easy to write an if-statement.

It's easy.

It's easy to write all these things.

The challenge comes in figuring out what you're going to actually code, like that's the hard part and the thinking about it and all the planning that goes into it.

That's what actually makes the coding so easy is the hard work you do before you actually begin the coding itself.

And we have a habit as developers and as businesses that employ developers even to put a high priority on the coding and not such a high priority on the planning that comes before it that is necessary.

And that's-- it's unfortunate because we actually need to spend a lot of time planning in order to code effectively.

So the biggest worry in all of us when you begin an application or when you begin rewriting an application is that you're going to end up doing the same work again, that everything you do, you're going to have to trash and then you'll be back at that blank Editor window.

So you have an entirely different set of worries when you're rewriting something that already exists.

This is the same intimidating Editor window but now, there's all this work that you did before that you're-- you've decided for whatever reason to throw away.

And that becomes a different set of challenges.

First of all, you want to know why you absolutely need to throw all this stuff away, because you don't want to throw something away that's working, obviously, if you don't need to.

So you want to be very clear on what you expect to get out of a rewrite.

You want to have a good idea of what you want to maintain in the current application, obviously, as well, and make sure that you don't end up regressing back to something that you-- to a state that you forgot that you had ever visited in the development of a previous application.

There's always the concern that once you get in there and start fixing this one big problem that you have with the existing application, you're going to find a bunch of other little problems and, like, all those things that have bugged you, like, for a long time and end up doing a lot of react shaving.

And then, you know, there's the same concern that you really don't know what you're doing.

OK but I'm not going to stand up here and tell you that I have, like, some bulletproof solution to keep you from ever having to rewrite anything again.

I wish I could tell you that, for my own purposes as well as for yours, but that wouldn't be honest because you're going to end up rewriting sometimes.

And it's not always a bad thing.

There are good reasons to rewrite something, certainly.

You can rewrite things if browsers change or if you're on the back end if Node changes so that you can take advantage of that new stuff.

And that's certainly a very good reason to rewrite something, if you can get a big advantage from whatever the new thing is that's available.

If your business grows, like, much faster or much larger than you thought it would and you have scalability problems that you need to rewrite to address, like, that's a good problem to have, right? Similarly, if your infrastructure changes, if you move from, like, a virtual private server to, like, your own RAC in a data center, like, that's great.

You have all this new stuff you can take advantage of.

Why not do it? You can get to certainly a point where you have so many refactors stacked up that you want to do tiny little things that you could do without rewriting everything that it makes more sense just to roll that into a rewrite.

I think that's a pretty edgy case but it could happen.

And I think the main reason, though, that we end up in a position where we're like, OK, we absolutely want to rewrite this thing is that this thing is pretty broken.

And if we don't, like-- if we don't stop the bleeding right now, then we're going to be doing a lot more troubleshooting and fixing of things than we would like to be doing, creating features or whatever else we need to do on our existing application.

So the counter to all that is the bad rewrite, which is the one we want to talk about avoiding today, which is where you have some percentage of your application already coded and you're going along.

And then, you're like-- you go into some-- it usually happens for me when I'm handling some error.

And then, I realize that I shouldn't be getting that error and I should have done something to prevent getting that error and I've actually written the entire application incorrectly, but any scenario where you end up throwing out all of your work and going back to the empty Editor window.

So the bottom line is we want to keep from having to start over and over and over and over.

And that's not inevitable.

There are things we can do to mitigate that and hopefully, we can do them all successfully and keep them all in mind so that we don't have to do the bad rewrites and trash all of our work.

So what I want to do now is go through a completely unordered and non-comprehensive list of all the bad rewrites that I have personally been involved in with things that we got some-- some way down the road and then were like, oh, this is wrong, and try to explain how that happened and what it taught me personally about avoiding that in the future.

So the first one is when you start-- you're sitting down to start developing your application or rewriting your application and you're like, OK, this is awesome.

We're going to use framework x.

And there's probably no reason not to use framework x.

That's an innocent enough thing to say, right? Hopefully, the framework that you've chosen, you can find a lot of information about it online.

People are excited about it.

It's interesting.

Perhaps it's something that somebody you work with has used successfully before and they've been able to, like, knock out an application in a weekend because, like, it has all these bells and whistles and takes care of all that boilerplate stuff and it's awesome.

Typically, the framework, one of the things it offers you is some concept of an architecture-- or not an architecture but a pattern that you can use in your application.

And you can generally find a way to make that mirror what you already have in your existing application or your plans.

For instance, if it's MvC, you can look at your back end and be like, oh, hey, we have a data store here and this data store contains tables or documents or whatever.

And gosh, those look a lot like models so check, we've got one third of our application done already because we've already got models.

And that's how, you know, your mind can end up working, same thing with event-driven architectures or whatever else.

You can find a way to make it fit the pattern.

So you start coding it and you're coding up a model and you're like, oh, this is great.

I just do, like, a little bit of initialization stuff and then the boilerplate takes care of the rest for me.

And I'm just, like, extending a little bit and then I'm going to, like, set a URL so I can do my routing or I can sync to the server and that's awesome.

And then, I just, like, extend the model object on the framework and that's great and this is super easy.

And then, you have to actually do stuff and that's where you can end up in a position where you find that you didn't maybe think this through as much as you thought you had.

You have to figure out how to do state objects and that's not part of whatever framework you've chosen.

You have to do event handling and that's not part of whatever framework you've chosen.

And you try to look this stuff up in the documentation and you find this awesome, great-looking to-do application but you're not writing a to-do application.

Or at least, you didn't think you were.

But the trouble is that it leaves you with all this stuff that you aren't really getting answers for with the framework and you don't exactly know how to use the framework effectively in combination with the things that you need to do.

So this is how frameworks work, right? They're not meant to solve all your problems.

They're meant to solve easy problems that are common enough that they can provide effective boilerplate for them and ease that stuff over so that you don't have to rewrite obvious stuff.

But they're not meant to tell you how to write your entire application and they don't typically tell you how to organize your application unless they're very opinionated.

Even those, though, at a certain point, rely on you to provide the organization for your own logic, the things that make your application your application.

And so what I've learned from this sort of mistake is that you should not choose your framework first.

That's what a lot of us tend to do.

We tend to say, oh, we're going to write a backbone application.

We're going to write an Angular application.

You don't know what kind of application you're going to write until you've actually figured out what it is that the application is going to do and all this other stuff about it.

And so I would propose to you that you choose your framework last instead of first.


So the kind of flip side of that is the second one, which is when you say, yeah, all these frameworks, like, they're kind of confusing and we don't really know what to pick so we'll just write our own.

And this can be very appealing.

There's certainly a lot of charm in the idea of a hand-rolled framework that suits your application specifically and takes care of exactly what you need to do rather than a bunch of boilerplate stuff.

Why not just get right into it? And you're going to know this code.

It's going to be all familiar to you because you've written it yourself.

So that's great.

And of course, you know, if there were things in one of these frameworks that you didn't find yourself needing or, like, paths that you didn't want to go down or wanted to combine different approaches, like, you can knock all that stuff out.

You don't have to include it and that's less weight, right? So this is great.

You start writing your framework and you're, like, this is going to be so awesome.

And so you write an initialization function.

You provide all the boilerplate for initialization.

That's super cool.

You put in a URL property and that's going to, like, provide routing and enable you to sync with the server.

And what you've done is you've rewritten something that probably already exists.

And it's difficult to find something new under the Sun that-- a problem that you're solving with your new spoke framework that somebody else hasn't solved before.

But the problem is that you're writing it for yourself now so you have no obligation to maintain the same API.

Or you can maintain the same API but have it work just slightly differently so that when you hire somebody new onto your team or somebody else has to support your code, they don't quite know what it's doing.

And there is no big to-do application for them to go and look at and try to figure out what it's supposed to be doing and there's no documentation, essentially.

So the other problem is, of course, that it's not easy to write a framework.

There's a reason that, you know, tons of developers work on these open-source projects.

There's a reason that they evolve over time, that they have versions.

It's because this is not something you just, like, sit down in a weekend and, like, knock out.

It takes time to create good abstractions and good boilerplate that can be used by many, many people and it's not trivial.

So this is a fairly obvious thing but I think it's the kind of thing that as developers, we sometimes find ourselves doing, anyway, just because it's kind of fun and it's appealing in and of itself but not when you have something to get done.

When you need to write an application, your application, you don't want to be writing a second application or the equivalent amount of work, which is, like, what you're basically doing as soon as you decide that you're going to write a framework.

So one application at a time is the lesson that I've taken from trying to write frameworks myself.

Number three is the idea that you can't possibly start defining your API until you start coding things.

And this is mainly because you know, you just-- you don't know how the code's going to evolve and you don't know what the application's going to look like.

You don't want to get yourself in a situation where you have to go through and make all these changes to the assumptions that you had in the API you outlined initially if you had done that, right? And then, there's also the idea that you're going to use the API as a place to optimize things.

You want to make sure that you're not calling the server too often.

You want to make sure that if there's stuff that takes a long time to do, that you do that on the server.

And if there's stuff that you have to do repeatedly, you do that on the client, right? All these considerations-- and so you-- how can you predict that without having written any code? So like, example, let's say that you have these three views and they all have to render but you want to render them all just a little bit differently.

You want to do different things with them.

You know that part.

And so you've got a concept of a render function but for every render, you want to do just something a little bit different and this all concerns the server.

So the first case, you want to go and get your data and then you want to populate a template that contains in-line logic that's going to say, like, how to pluralize things or how to-- what other kinds of conditionals that exist in the template.

So all that logic, all that data transformation stuff lives within the template and you're just getting the pure data from the server.

The second one, you're going to go and get the data from the server also.

But in this case, you want that data to be transformed for you on the server because you have other things on the server that need that same logic.

And then, you're just going to do the same thing.

You'll stick it in your templates, stick it in your HTML.

But the third case is special.

The third view is something that takes a fairly long time to render so you want to get that all from the server.

You want to get the data already populated in the template and just stick the result into your HTML directly.

And you get yourself in a situation where it's really hard to predict what your server is going to do.

It's hard to predict what your render function does on the client now, in addition.

And you've basically signed yourself up to create a brand new function for every view because you don't have anything in there that you can reuse.

There's no clear way to abstract that out so that you can just have one render function that behaves the same way.

And again, there-- there may be arguments for optimizing these things, sometimes, but you probably want to figure that out after the fact, as an exception to an existing pattern.

And so this doesn't give you the opportunity to create that pattern if don't-- if you aren't willing to decide on an API and decide how your API should work fairly early on.

So the lesson there is simple.

An API is basically pretty easy to specify.

And yeah, you might end up changing it but you're not changing that much.

I mean, really, you're changing where it goes, what you pass it, and what you expect back in return.

Those are not complicated things to change.

The API itself, the definition of the API, doesn't really care about the client-side application or the server-side application.

It's the point where they touch and that's fairly simple.

That's not a lot of work to trash.

Even if your assumptions were all completely wrong and you had to change the entire API that you had specked out, you don't lose that much.

So it's a cheap way to try and figure out what your application is doing.

So the fourth one is, again, the opposite of that, where you say, OK, everything got really screwy, like, when we started passing things back and forth.

So let's just say that if it isn't actually JavaScript, then we're just going to do the whole thing in whatever our back-end language is.

And again, this can give you some great optimizations and certainly, you want to have your static stuff on the server.

You want to have your dynamic stuff on the client.

That makes sense.

And then, everybody can write in their own language and everybody's more comfortable.

So that's good, right? But you get into-- you get into trouble and one of my favorite examples has to do with templates.

So like, let's say you have this template on a server and it's an error template.

What you currently do with it is when somebody submits a request, some form or something, then you put that error into the template that you're returning and you send it back down to the client and it displays, simple enough.

But you determine that you actually need to use that on the client.

You need the client to be able to render the error without having to go back to the server for the entire page.

So hey, I mean, it's a tiny little error thing.

It's, like, three lines of code.

You're like, oh, we'll just copy this into JavaScript.

So yes, we'll have the template in two places.

Oh, we're bad.

But you do it because it's not very expensive, right? And then, some time goes by and you determine that, oh, you need to change some of the classes in this error template thing.

And so you add some classes into the client-side one, you add some classes into the server-side one, and then you figure out that you need to have, like, a tool tip within it somewhere to explain some really interesting details of the error.

And so you need be able to pop that tool tip up so you need to add the hooks into the template itself so that you can create it.

And then, you need to create a way to register it on the server so the server has to, like, register it in a different way than the client, which will register it within the flow of the client-side application.

And eventually, you get to a point where you're like, we should have just used the same template.

We should have just figured out some way to be able to share this template because this is getting to be a pain in the butt.

So the lesson that I have learned actually probably more than any of the other nine that we're going to talk about or we have talked about is that you have to plan everything in your application.

And that doesn't mean just JavaScript and it doesn't mean just the server-side stuff.

You need to plan your HTML.

You need to plan your CSS because your CSS impacts your widgets.

Anything that you're doing that requires a high amount of interactivity, that's going to be affected by your CSS.

So all of this stuff is part of planning your application.

It's not-- it's not just templates.

It's not just, like, resources.

It's all something that you should be factoring into your architecture.

The fifth one is when you look at the DOM and you're like, oh, well, this doesn't work with our web application.

So what we're going to do is try to find a way around it.

And there are definitely a lot of inconsistencies in browsers.

The DOM is unfortunately still not perfect and that can lead you to writing a lot of extra code to, like, wrap around it and kind of try to make things more consistent.

And even when the spec sounds good, like, you might find that the actual implementation is not quite lining up in different browsers, et cetera.

That's a problem.

And so it can be kind of tempting to be like, hey, we're just going to throw all that stuff out and we want to write our own DOM from scratch.

We want to write our own widget that supplants whatever the DOM is normally doing in this situation.

My favorite classic example would be the file uploader.

So like, you've determined that you need, like, a special, cool file uploader and you can do a lot of this cool stuff right now with modern browsers.

But you want to make sure you can do it everywhere and you know, just kind of do it once.

And so you're like, we'll just write our own.

And so you make a little place to select a file and then you style it.

You give it, like, rounded corners and make it orange and whatever.

You add progress events because that's always awesome so the user can see, like, how long it's taking to upload their stuff.

And then, you create some sort of, like, Flash movie or something that actually enables you to do that because you don't have access to these things across the board in all the browsers you want to support natively.

And then, you have to do some other stuff because you, of course, want this to work within your application, right? And so you create some sort of handler for it that's going to allow you to connect to the stuff in the little Flash object that you have and you want to be able to submit it as part of a larger form.

You don't want to have to just, like, upload these things as one-offs.

You want to be able to, like, use it within a form if that's what you need to do.

So you add that stuff.

And then, you figure out that OK, that's not actually working for everybody as well as we thought.

It's working for a lot of people, which is cool, but we need to be able to degrade it if, for instance, Flash isn't available.

So we'll fall back to just, like, a normal file upload if there's no Flash and then we'll see if we have XHR2 so we can always do the progress stuff.

And then, we want to, like, change that around.

Put some wrappers around all that DOM stuff so it matches the Flash theme we wrote and then, again, let it be used in a bigger form, et cetera.

And this used to be, like, totally reasonable because really, browsers didn't use to move as fast as they did and we were not getting the powerful stuff that we have today.

Now, though, like, if it's something that you thought of doing-- and I don't want to, like-- disparage anybody's ingenuity or anything-- but if it's something you thought of doing and something that you've determined that your users need you to do, there's a pretty good chance that, like, some data browser company is working on it already.

And in the meantime, like while those things are getting developed, you have people creating poly-fills.

You have people already solving this problem in a very abstract way whose work you can go and grab and, like, at least rely on that.

And you know, possibly, depending on the complexity of what you're trying to do, like how common the widget that you're trying to create is, there's probably somebody out there who's, like, made a little plug-in that, like, does the orange corner rounded stuff-- transforms whatever object it is that you're working with into whatever you want it to be or at least gives you access to do so yourself.

So if it's not your job to make widgets, if you don't sell widgets, then probably it's not a great investment of your time to be rewriting the DOM.

This goes back to the idea that you only want to write one application at a time because this sort of thing can get very involved and it involves a lot of testing.

It involves a lot of other work that probably isn't getting you any further with your application, especially because most applications do not tend to be primarily interaction-driven.

Some are and they're cool and maybe in those cases, you are actually in the widget business.

But if it's a standard application with, like, a log-in and some forms and stuff, like, probably it's not a good use of your time.


So the sixth one is trying to balance what you're getting via XHRs with what you're getting in your HTML.

And what I mean by that is you start thinking that, hey, we're already getting all this HTML, anyway, right? And we have all this data on a server, which is where the HTML's coming from.

So let's put the data in the HTML.

We have, like, lists of things and we don't know how many things we're going to have.

We don't know whether we're ever going to need them so let's put all the information about those things in attributes on the DOM elements themselves.

Similarly with internationalization-- similarly with, hey, your entire application state.

This is not something I'm making up, being able to take your-- the state of the application as it comes down from a server, put in a bunch of meta tags in the top of the page, and get it that way.

And that sounds really cool at the beginning because you're like, hey, we're going to initialize this stuff and we don't have to pass anything in and we don't have to figure out, like, if there's some object that we've already passed in that's, like, been hard-coded into the JavaScript.

We just go and get the stuff from the page.

We'll go select the necessary nodes and we'll find their attributes.

And then, we'll send those data types and yay.

And that's cool until, of course, something changes.

And then, when something changes, then you have to go and find those nodes again and then you have to set the attributes so that they line up with whatever changed in your data in your application.

And then, you want to send that back to the server so it's consistent.

So you send it back to the server.

And then, if you get some sort of error, then you want to like, whoa, roll back.

Find the nodes again.

Set their attributes back to what they were before so you're assuming that you've maintained the state of whatever they were initially before you change them and blah, blah, blah, and et cetera.

The DOM is not a good place to do that.

The DOM is a bad place for your data.

It's kind of painful, first of all, to just go through and be selecting objects all the time in order to get your data.

It's kind of not natural.

It's messy and fragile.

You're relying on selectors and the hierarchy of the elements in your page.

Obviously, you're violating DRY because in addition to-- in addition to having the data that you're using within your application, now you have to maintain it, again, within the HTML elements or they're going to get out of sync.

And then, if you have something that relies on the HTML elements, which is what you've set up your application to do, then you won't be getting accurate information anymore.

But mostly, it just doesn't give you anything you can work with.

You have the opportunity to pass down whatever you want from the server and you're sending yourself HTML, which is not what you need.

That doesn't seem like the best way to plan that.

So give your data a real home and I can think of probably other examples of ways that people try to get around, like, actually using data as data within their application so that they can kind of get a two-for-one mostly when they initialize things.

But the bottom line is that you should be separating your data out and using it as data, recognizing it as data from the get-go, so that you can then put it in your objects and your client-side application can use it the way it's meant to be used.

So the seventh one is when you're like, hey, we've got some weird piece of logic or some strange task that we need to do and there's a plug-in that gets us, like, part of the way there or there's a module or whatever.

And certainly, you don't want to go around reinventing the wheel.

So this is pretty appealing, right, because there's all this open-source work and it's great and people have contributed to it.

Most importantly, they vetted it for you so you already know that other people are using this successful and it's worked for some people.

And it's easy to think that maybe, hey, as long as it gets us part of the way there, that's better than none of the way there.

So why not, like, stand on these people's shoulders and try to move ourselves further along? So you have, like, some very important function and you need to pass something into it.

You make it a jQuery object and then you have this plug-in that you apply to it.

And then, now you get some sort of event back from the plug-in, which is what you needed.

And so when you get that event, then you can do your really important function or your super important function.

And then, in your super important function, you can do the main thing that you need, which is to get this important value back from the plug-in.

And then, you have to do a bunch of other stuff, too.

You have to add some special state that's unique to your application and you have to remove the weird, funky class that the plug-in adds to itself.

you need to append some of your own markup that you have somewhere in somewhere else to some part of the plug-in and it's adding some weird button.

You need to disable that.

And you're kind of finding that this is all a nightmare.

So you don't want to do that anymore.

So you go back into the plug-in file itself and you just modify it because you're tired of doing this every time, like, some event happens or some other situation occurs.

So you want it to just work the way you want it to work and so you change the plug-in.

And now, what you're doing is not writing a whole new application but writing a whole new plug-in because you're not actually in control of what you had initially and you're having to create your own plug-in essentially around that.

Initially-- additionally, that original might get abandoned.

And so then, you really will be in charge of the plug-in because there will be nobody else working on it with you.

So that would be fine, I guess, but as soon as you start doing this, you're not getting a lot of value from whatever you originally downloaded.

And figuring that out too late really sucks because then you've written everything to the API of this plug-in that you only kind of halfway like.

And you have a bunch of code that you essentially need to rip out because you could have written your own thing that would do exactly what you needed and nothing more, nothing you'd have to screw around with, and that would have saved you a lot of time in the long run.

So you don't want to use external tools that get you close to doing something or that, like, sort of do things you like but then sort of do things you don't like.

Don't go grabbing external tools because you think that they're going to get you further along than you could get on your own because as soon as something does something that you don't want-- there's some undesirable aspect to it.

It's not super granular and atomic and just fits into your application-- making it fit into your application is probably not worth the trouble and can lead you back to a place where you have to rip it out.

And then, you're rewriting.

So the eighth way is to get yourself thinking that because you have a framework and that framework has some sort of pattern, you don't need dependency management.

I think that this comes from the idea that, like, when you grab your framework, you don't see it using dependency management in its examples or, like, in its files or whatever.

I think that's where this comes from.

I'm kind of postulating.

I've done this, too, and I can't say what I was thinking at the time.

But anyway, I think you get this idea in your head that since you have, like, this one big controller or something, you have this one main file and you know where everything's going to start and you know what's happening there.

So you know what the dependencies are already.

You can just sort of concatenate things, send them down to the client, and you're good to go.

If the issue of, like, a dependency loader comes up, you already have some sort of framework thing that's, like, doing your loading stuff, that's, like, taking care of your calls to the server.

And so you can rely on that.

It's not a big deal.

It fits into your application.

And then, if it gets really slow, then you can cut out another JPEG or something like that.

You've heard good advice on this so you're like-- you're fairly confident that it's not going to be a big issue.

So you have, like, some big controller.

It's got a big view that renders everything on your page and you get into trouble because you put that render everything function in a different file and you forgot to load that one before loading your big view.

And so you have to change the order of that.

And then, you've got-- you want to be able to Refresh the entire view.

So you want to be able to pass a Boolean into the controller.

And if you need to Refresh it, then you want to set the data on the view before you render it.

Oh, but you're using a global to do that because it just made sense because that's, like, the data for the entire application.

So you wanted it to be available to everything but you forgot to define it in one of these cases.

So you need to make sure that the data's available and then if it's available, then you want to set it.

If it's not available, then you want to go back to the server, be like, give me that data, please, and then, set the data and then render it.

And then, you get an error.

If you guys look back at this, you can probably see what the real error would be.

But basically, you need to obviously move all the logic now into that handler for the XHR so that you can do it after the data's actually loaded.

So if you don't have modularity, you can get a lot of messy code, basically.

And that probably sounds like every AMD talk you've ever heard but I think that's what it boils down to.

You certainly don't want to be relying on globals or sub-globals.

It's cool they're, like, easily accessible but like, it would be just as easy to access them by passing them into things as dependencies.

It puts you in a situation where you have to keep testing to see whether things are available.

You're not ever really sure what's available if you're in a situation where you can load something faster than something else or something can load before something else or it can run before something's fully loaded, et cetera.

It would be much nicer just have a clear contract with the module that says, I will expect you to load all these things for me.

And every time you come-- you hit some edge case or you want to add something or whatever, you have to go and reexamine that whole chain of the dependencies that you're loading in order in a sort of fragile way to make sure that you're going to look things when you need them loaded.

And similarly, if you want to do-- if you want to do actual dependency loading on the client or something else like that, you can't abstract that out.

There's no nice file that you can or nice program that you can offload that to.

You have to take care of it yourself and go and fetch, like, things like templates or things like CSS files that you want to swap in.

All of those you have to do manually.

So just save yourself some time and assume everything will be a module.

I did a very big rewrite that I never finished just to add a Require to everything and it's not as easy as just wrapping everything in Require.

It's more complicated than that because you actually change the way you think about your code.

You change the way you think about what's available when and it's not trivial to go back and add it later.

So you should add it from the get-go.


So the ninth one is when you have your existing application, your existing application contains something really terrible, and you're like, hey, this tool or this framework or this whatever works with that terrible thing.

So yay.

And this sounds like a way to limit your risk, right, because you can save yourself some time, save yourself some code that you would have to rewrite, and just focus on the things that you actually wanted to improve.

And all of the work that went into your original application, you can reuse again.

So you've got like, some old function here and it, like, takes the output of your terrible thing, transforms it, renders it, observes it, and you had to write all the modules to do that and it was a huge pain.

And so you grab a new framework that works with your terrible thing and then you can transform that output natively within the framework and then render and observe it.

And you don't really get a lot from this.

The problem is that when you go in with an assumption that you want to preserve something in your original application, then that's something that you're not improving.

And over time, especially if it's a large application, you can end up basically just rewriting the same thing and not doing a whole lot to change what you're getting out of it.

And the real problem with this is that then you get done with that, you realize how much it looks like the old one, and then you're like, man, we need to rewrite this.

So just throw out all your assumptions.

When you begin a rewrite, like if you've already determined you need to rewrite something, throw out all the assumptions about how things are going to work.

Definitely keep the whys, like all the user cases and all that stuff, like all the things that you need to maintain that are actual behaviors of the application.

But everything about how it works, like, just trash all that and, like, see if you can start from scratch as much as possible because that way, you can hopefully get a lot more value out of the fact that you're rewriting the whole thing.

OK, the 10th one, 10th and final one, is like, we found this really awesome thing.

What can we use it for? So there are a lot of really awesome things out there on the internet and if you pay any attention to Twitter or Hacker News, you'll find tons of them probably every day.

And they've got a lot going for them.

They've got a lot of nice features.

They're fast, they're modern, great communities, good documentation, and that's all well and good.

It means nothing if they don't solve your problem.

And that's an easy thing to forget.

That sounds really obvious when I say it, I'm sure, but it's easy to forget if you haven't taken the time to define your problem.

And I don't mean just saying, oh, well, the user needs to be able to access their account.

Obviously, they need to be able to access their account but that is not a definition of the problem.

That is a very general page in your application.

You have to actually break that down into all the pieces of what that means.

What happens when they access their account? What if they're not logged in? What if their accounts suspended, et cetera? And you need to break those things down further into points, like what's going to happen when I click this link? What's going to happen with this tool tip? What happens if I'm an old browser? All of this stuff, like, needs to be thought through, like, to a degree that is perhaps a little insane.

But, like, if you really want to figure out if you need to use something, the best way to do that is to have a very clear definition of your problem that, like, goes well beyond just, like, your classic user story.

As a user, I want to do some cool thing and use this widget.

And you want to help yourself avoid solutions that are irrelevant to what you actually are trying to do.

So I've been asked a number of times to proof of concept something.

We've gotten to talking about whatever it is, some tool, and then the proof of concept has ended up being, oh, I can prove that this works because I installed it and it ran.

Or I can prove that it works on our server.

Or I can prove that it can be scripted with JavaScripts.

And these proof of concepts don't solve anything.

Really, the best way to proof of concept something is to figure out if your application actually needs it and then, hopefully, you can rely on its community and the reputation it has on the internet to figure out that, yes, it will work for you.


So that's the end of-- that's the end of the examples.

But I hope you will agree with me that none of those sound like particularly desirable things to have.

None of that stuff is something that we want more of in our application.

But again, none of these are uncommon.

It's not just me.

I've had colleagues who have done all of those things, as well.

I've had peers and friends.

These are not, like, things I just made up and pulled out of thin air.

They are real things that happen in the real world all the time all around you because it's really hard to avoid rewriting completely.

There are all these things that we do as developers and all these sort of, like, Achilles heels we have when it comes to being able to think about our own code critically that make us really susceptible to, like, going down rabbit holes like these.

But hopefully, you can avoid doing it more than once.

Hopefully, if you start doing it, you start with a bad rewrite-- something that's going to be a bad rewrite, hopefully you catch yourself and then be like, oh, yeah, I have to be more rigorous about this.

I have to really make sure that I think this stuff through.

So you need to plan your rewrites, essentially.

Not just plan what you're going to rewrite, plan the rewrite itself.

You need to figure out if you have the resources, meaning the developers and the hours in the day.

You need to, again, define your problems.

Define them to an insanely, like, detailed level.

Make sure that you have regression tests.

And the most important thing is make sure that you actually want to commit to doing the rewrite and all the planning and all the work that that's going to involve because there's another thing that I left off that list, which is I would say the main first and foremost way to rewrite a lot.

And that is getting cold feet halfway through it.

So don't-- you need to commit to that stuff.

You can't expect that it's going to happen immediately.

It's going to take a lot of time.

Don't expect to be able to iterate because if you start, like, trying to pull out pieces of your application and just rewrite that and keep everything atomic, you're going to, again, do the same thing where you end up writing the same application because you haven't actually changed the architecture.

You've just changed it in chunks.

Failing fast-- do not fail fast.

That's what your application's for.

That's what product is for.

When you're developing a product, it makes sense to fail fast.

You want to figure out if something's going to work really early on and then abandon it if it doesn't.

This is not product planning.

This is essentially a lot closer to computer science.

You have a working product.

You know what the product needs to do for the users.

You need to figure out how to make the code better and that doesn't take-- that doesn't take proving.

That takes thinking and planning.

Then, the bottom line is really that you don't want to-- you don't want to put your fingers on the keyboard until you have that plan in place.

And essentially, what I'm saying is this is not rapid application development.

It's something completely different so don't get confused.

Don't think about all those rapid application development lessons you learned whenever-- in start-up school.

Just ignore all that stuff because you have to have a plan you can commit to, which is sort of the opposite of rapid application development, or it isn't a good plan.

And all that's going to keep you from getting to 10 rewrites is essentially a good plan.

So that's it.


[APPLAUSE] That was great.

I think all of us watching were like, wow, like, everything that you're saying are these things-- these problems and mistakes that I've run into.

And so everyone kind of empathized with all those challenges.

It was a really good summary of all that pain.

A few questions-- first up, this one's a bit practical, which is-- yeah, go for it-- how would you embed-- what is your proposal for basically shipping down data with HTML, of essentially making use of data attributes? Or how do you feel about throwing JSON and a data attribute but kind of delivering it in the same request? In the same-- in the same request, I think that's-- OK, wait.

So in the same request is great.

Twitter does that, right? They throw down the HTML all rendered and then they also have, like, just a JSON object that is just a JSON object.

That's cool, like, as long as you're keeping it in, like, JSON or something, I would say.

But if it's, like-- if it's data about the HTML, like, sure, like, put it in a data attribute.

But if it's data about, like, some object in your JavaScript in your client-side application, I would say that you should probably find a way to make it JavaScript.


One confusing thing about the Twitter-- the web Twitter UIs, you have to send down these enormous JavaScript objects.

And then, I think they even have HTML inside of the JSON object, which is inside of a data attribute.

So like, you'll be viewing source and you'll be seeing HTML and then you're, like, trying to edit it.

But then, you realize that it's actually, like, a big JSON object and you get really confused really quickly.

But yeah, they've spent a lot of time on that.

So you talked a little bit about making use of established widgets so you don't run into, you know, all the edge cases that they've already handled.


When would you advocate rolling your own solution? [LAUGHING] I guess when there's nothing else out there, like there's literally no library you can build off of and the browser implementations are still fragmented enough that you can make them line up and you really need a solution that is solid in order to get your work done.

We talked a little bit about this at the party.

I'm actually doing-- considering doing something very similar at work because we have to deal with content-editable and there's really not a lot out there.



One question that was asked was-- and this is something that you hear a lot-- depending on frameworks-- there's a fear that depending on frameworks and a concern that you don't learn the basics.

People know Angular instead of doing JavaScript.

And you know, three years ago, it was knowing jQuery instead of JavaScript.


Is that something that you're concerned with or how do you feel about that? I don't think-- I mean, I'm not concerned about it personally.

I think if it gets you there, like, if it gets you to feeling more comfortable with JavaScript, awesome.

And if you can produce something, then you're going to feel more comfortable with JavaScript and feel like you've got this and hopefully then go back in, like, the framework.

And I'm definitely not concerned about it from in this context.



Last question, about templating-- you actually-- you made this really fantastic template chooser web app.

You can, like, go in.

You answer a bunch of questions about, like, what your templating needs are and it basically filters down about 20 or so templating libraries and indicates which kind of meet your criteria.

What is your recommendation for having a templating solution that spans client and server? Well, use Node if you can.

If that's not a possibility, like, Mustache, has, like, a variant for, like, every template or every language under the sun.

The only problem with those is that, like, a lot of the time, like, you start dealing with your data and then, like, you run into the way that different languages deal with the data itself.

And so you have to use data in a very simple way and you have to have your data all transformed if you want to-- if you want to share between two languages.


I think there was actually-- there was-- somewhat recently, there was, like, a bug in the Mustache implementation for Ruby, where it was-- oh, no, it was some YAML-- it was a YAML library in Ruby and it was a security issue.

But yeah, I can understand different implementations across languages.

What templating library do you use? I like-- I still like doT.

doT? doT, yeah.

But there's a really cool one that I'm keeping an eye on.

It's called Nunjucks.

It's based on-- Nunjucks? Nunjucks, yeah.

Nunjucks? Yeah.


But nunchucks-- but it provides, like, a wrapper around the template.

So it's more than just the template.

It's like all the stuff that goes with it, like the transformation logic and, like-- Whoa.

To some extent-- like, I'm hazy on, like, how much of this is, like, stuff that it actually does and how much of it is my fantasy of, like, a perfect template engine.

But yeah, it does more stuff.



That sounds great.

All right.

Well, thank you very much, Garann, and this was great.

Thank you.



Post a comment