disquisition.net

Photo by Annie Spratt / Unsplash

↜ Blog

Angular 2 Postmortem

June 17th, 2017

I’ve spent a long time us­ing, read­ing about, and think­ing about React, so when the time came to de­cide the fu­ture of front-end de­vel­op­ment at Apartments.com I pushed hard in that di­rec­tion. However, af­ter a long se­ries of dis­cus­sions, we ended up set­tling on Angular 2 (hereafter referred to sim­ply as “Angular”).

I ad­mit I was dis­ap­pointed, but given that our ex­ist­ing front-end is built mostly with jQuery and knock­out.js, and is def­i­nitely show­ing its age, al­most any­thing else would be a big step for­ward. In any event, I have some dis­tance from our first ma­jor pro­ject and be­low are some thoughts that stick out from the ini­tial foray.

Webpack

Like most cur­rent front-end build pipelines, Webpack played a cen­tral role in our bundling process. Most of our JavaScript code at Apartments.com is pack­aged in a cus­tom AMD-style JIT bundler, so mov­ing to Webpack and ES2015-style import state­ments rep­re­sented a sig­nif­i­cant shift in our de­vel­op­ment and de­ploy­ment work­flows.

We iden­ti­fied a num­ber of po­ten­tial hur­dles early on but the only is­sue we ended up hav­ing was Webpack com­pi­la­tion time. Waiting for 20 to 30 sec­onds for Webpack to re­build its bun­dles af­ter a code change was a very neg­a­tive ex­pe­ri­ence. We worked to im­prove com­pi­la­tion time but 20 sec­onds re­mains the rough av­er­age. Based on per­sonal ex­pe­ri­ence, this seems long, so I hes­i­tate to blame Webpack itself, but we have yet to iden­tify the un­der­ly­ing is­sue.

Currently, we have our Webpacked apps run­ning right along­side our legacy code, with no mean­ing­ful in­terop, but the so­lu­tion we’ve dis­cussed when we get to the point where Webpack/legacy in­terop is a ne­ces­sity is to out­put our Webpack apps as UMD bun­dles and con­sume them at the en­try point of the legacy app (using the externals Webpack con­fig­u­ra­tion op­tion to al­low legacy mod­ules to be used within the Webpacked code).

TypeScript

The Angular docs say you don’t need TypeScript, but I don’t think they’re fool­ing any­one. Luckily, TypeScript is a mas­sive breath of fresh air for any­one used to work­ing with a strongly-typed lan­guage. Especially for large teams, strong types can be a huge main­te­nance boon.

That said, us­ing TypeScript—currently[1]—precludes the use of the in­cred­i­bly rich Babel ecosys­tem. At the very least, babel-preset-env is one the most in­tu­itive ways to man­age poly­fills and code trans­forms and I don’t see any­thing like that end­ing up in the TypeScript com­piler any­time soon.

RxJS

The role of RxJS is down­played a bit in the Angular docs, which led us to put off team RxJS train­ing un­til late in our de­vel­op­ment cy­cle. This was a mis­take. Most of our devs were un­fa­mil­iar with Observables and the RxJS li­brary of op­er­a­tors, and fail­ing to for­mally in­tro­duce these con­cepts as a core part of the frame­work lead to some pain later in the sprint.

That said, RxJS Observables are in­cred­i­bly use­ful. I was re­luc­tant to em­brace Ob­serv­ables at first sim­ply be­cause I was al­ready very com­fort­able us­ing Promises and a sec­ond asyn­chro­nous prim­i­tive seemed re­dun­dant. However, once I wrapped my mind around their use and got com­fort­able with a hand­ful of op­er­a­tors, I found my­self re­ally en­joy­ing the er­gonom­ics.

Dependency Injection

It’s worth tak­ing a mo­ment to talk about Angular’s de­pen­dency in­jec­tion mech­a­nism. Angular is the only ma­jor front-end frame­work I’m fa­mil­iar with that uses con­struc­tor in­jec­tion. The only real rea­son this is im­por­tant is be­cause con­struc­tor in­jec­tion is very com­mon out­side of the JavaScript world. This makes An­gu­lar more ap­proach­able for those whom JavaScript is a sec­ond lan­guage.

The trade­off, of course, is that every­thing needs to be reg­is­tered in a DI container some­where for any of it to work. Our teams worked ex­ten­sively with Unity DI in the .NET frame­work, so for us this is noth­ing new, but it feels un­usual for a JavaScript frame­work.

Bottom line, Angular’s DI is a small fea­ture, but if your team is most fa­mil­iar with con­struc­tor in­jec­tion, it’s one of those things that will make them feel im­me­di­ately at home.

ngrx/store

Being a React evan­ge­list, it prob­a­bly comes as no sur­prise that I found the pop­u­lar ngrx/store li­brary ap­peal­ing. If you’re un­fa­mil­iar, ngrx/store is a Re­dux-style state man­age­ment so­lu­tion with an op­tional plu­gin that uses RxJS Obervables (à la redux-observable) to han­dle asyn­chro­nous be­hav­ior. This was all mu­sic to my ears, but the Redux pat­tern is gen­er­ally mis­un­der­stood, es­pe­cially out­side the React com­mu­nity, so it took some ef­fort to on­board the team.

The dis­cus­sions we had re­gard­ing ngrx/store gen­er­ally cen­tered on one of two main points of com­par­i­son: our older pub/​sub event­ing ar­chi­tec­ture on one hand, and man­ag­ing state by us­ing a num­ber of generic Angular ser­vices. Most of the team was ea­ger to move away from the pub/​sub model since in our ex­pe­ri­ence it scales poorly, and the more we dis­cussed generic ser­vices the more it seemed like we would just be repli­cat­ing the ngrx/store API.

I don’t think every­one on the team was com­pletely com­fort­able with the pat­tern, but we did get to a point where every­one agreed they could see the po­ten­tial ben­e­fits.

Rough Edges

Okay, let’s be real for a sec­ond: Angular is in­cred­i­bly ov­erengi­neered. If you’re reach­ing for Angular be­cause you’re des­per­ately search­ing for some­thing that is­n’t React, take a se­ri­ous look at Ember or Vue.js. Then maybe take one more quick look at React.

Angular is a heavy frame­work. Angular is a frame­work pop­u­lated with nu­mer­ous dis­parate ar­chi­tec­tural con­cepts—com­po­nents, ser­vices, pipes, etc.—and most of these re­quire their own spe­cial­ized con­fig­u­ra­tion. App con­fig­u­ra­tion is most oner­ous early in the de­vel­op­ment cy­cle, but it never goes away en­tirely.[2] Routing has its own spe­cial con­fig­u­ra­tion. Test­ing re­quires a ton of spe­cial setup and con­fig­u­ra­tion. The tem­plate syn­tax is ter­ri­ble, and AOT is more trou­ble than it should be.

It’s not like other frame­works don’t have their rough edges and quirks, or that there are no rea­sons to choose Angular over the com­pe­ti­tion. Angular does DI really, re­ally well. The async unit test­ing util­i­ties are awe­some. But com­ing from React, where adding a new com­po­nent is as sim­ple as typ­ing export default () => <p>Hello World!</p>;[3] and hit­ting save, the day to day of work­ing with Angular feels slow. Plus, the two best parts of our An­gu­lar ex­pe­ri­ence—Type­Script and RxJS—can be used with­out Angular it­self.

Final Thoughts

Angular is a frame­work bur­dened by its past. For all the changes Angular 2 brought, it ad­heres to much of the de­sign phi­los­o­phy of Angular 1.6, even as much of the JavaScript com­mu­nity—in­clud­ing one of Angular’s own con­tem­po­raries, Ember—has grown be­yond that era. Angular will find a home in the C#/.NET com­mu­nity, but I don’t think it will ap­peal to ded­i­cated front-end en­gi­neers. There are just eas­ier ways to get things done.


  1. Babel may soon get a TypeScript parser. ↩︎

  2. It be­came ob­vi­ous very early in our first sprint work­ing with Angular that need­ing to con­stantly main­tain the top-level mod­ule con­fig­u­ra­tion was un­ten­able. We ended up writ­ing a cus­tom build step that in­volved de­tect­ing mod­ule files and then scan­ning .ts files in that di­rec­tory and its sub­di­rec­to­ries, build­ing up a cat­a­logue of ref­er­ences to providers and de­c­la­ra­tions, and fi­nally gen­er­at­ing the con­fig­u­ra­tion that nor­mally each dev would have to hand-write. This took about four days of dev be­tween a coworker and my­self. I was happy with the re­sult, but need­ing to work this hard to smooth out a frame­work’s rough edges is a huge red flag. ↩︎

  3. Okay, you prob­a­bly also need import React from 'react';. That’s not the point. ↩︎