Heraclitus Slays Zalgo
This is a 5-minute lightning talk I gave at Fullstack Fest 2018, which combines my trademark uneducated armchair philosphy with some JS jargon.
The Greek philosopher Heraclitus once said:
Upon those that step into the same river, different and again different waters flow.
In this talk, I discuss how the problem of “releasing Zalgo” (h͢e̴ ̵wh͝o ẁa̧i̛t̕s͟ ̵b͠ȩhi̷n̡d t͡h̵e͡ ͡wall) when working with APIs that are unpredicably asynchronous no longer applies when using Promises. Since correct use of a
Promise involves lifting all operations that depend on the resolved value into the context of the value (using
.then()), your program cannot end up in an inconsistent state. This is in contrast to earlier approaches, such as using callbacks, that would have involved modifying outside state (a mutable identity) that is referred to by other parts of the program, thus creating the potential for a race condition.
Apologies up-front that I’m not particularly polished or easy to follow here. I decided to do this talk on a whim, and it essentially became a brain-dump of things I had been thinking about recently ¯\(ツ)/¯
Here are the articles and talks I refer to:
- Are We There Yet?, by Rich Hickey
- Designing APIs for Asynchrony, by Isaac Schlueter
- Intentionally unleashing Zalgo with synchronous promises, by Daniel Brain
Dividing Frontend from Backend is an Antipattern
We software developers have historically used the terms “frontend” and “backend” to describe work on client-side (e.g., browser) and server-side applications, respectively. This conceptual split has evolved into the creation of specialized developer roles for each, which is still the norm throughout the industry. In practice, this is an arbitrary split that is too often used to avoid work we don’t want to do. It creates team dynamics that make it unnecessarily difficult to integrate client and server-side functionality and deliver quality software. This is particularly harmful in modern teams, which face increasingly complex client-side work.
Dividing frontend from backend is an antipattern. If you are someone who still feels that there is merit to a hard distinction between these roles, this article is for you.
We already do the same work in the frontend and the backend
What does a frontend dev actually do that’s so different from a backender? The feeling seems to be that backend devs have historically done the more ‘serious’ programming, thinking about meaningful issues such as testability, maintainability, persistence, asynchronicity, state management, and API design, while their frontend colleagues flitted about making things look pretty. If frontend devs did any serious coding at all, it was probably some hacky, half-understood jQuery, cobbled together from various Stack Overflow posts. Many folks still think along these lines.
But we don’t just share the same problems — we also solve them in a similar way. Functional abstractions are emerging as universally useful, such that we can often use the same concepts and syntax in the client and the server. Just look at ReactiveX, now implemented in so many languages that you could easily build a project on the same reactive abstractions in the front and the back. Then there’s the revolution of declarative rendering, which has made the view layer far more testable and stable. On the architecture side, microfrontends have become so popular that we’ve added them to the ThoughtWorks Technology Radar alongside their backend equivalent, microservices. It doesn’t make sense to keep the distinction between frontend and backend development when we’re all doing such similar work.
We need to build broad frontend capability to future-proof our companies
At the end of the day, a lot of us simply dislike styling and other areas of frontend that come uncomfortably close to design. Last year I heard a conference speaker quip that she shouldn’t have to care about something as unimportant as ‘presentation logic’. We have evolved a culture of disdain for frontend which protects us from having to learn since it’s not ‘real coding’ anyway. It’s an attitude that makes it hard to keep up with the market and continue to create meaningful products for humans.
We should care about how our programs consumed
As proud craftspeople, we should care about how our programs are consumed, whether through an API or a UI. At ThoughtWorks, we think of our roles as fluid and non-exclusive areas of championship. For example, while we usually have dedicated QAs on our teams, we consider quality to be everyone’s responsibility. In the same way, we should take collective responsibility for the full stack of technologies we use, and thus ultimately for the quality of the product we produce. Successful engineering organizations understand this. Tech legend Kent Beck, the inventor of Extreme Programming and signatory to the original Agile Manifesto, has said that hanging on his office wall is a poster bearing the following sacred mantra: “Nothing at Facebook is somebody else’s problem”.
Contemporary frontend work has evolved in complexity to the extent that we should no longer separate frontend from backend roles. Frontend engineers now solve the same kinds of problems as their backend counterparts, using the same kinds of solutions, and it is harmful to continue arbitrarily dividing us.
To be clear, I’m not saying that we all need to be experts in everything. That would be impossible. Today’s technology stack goes down a long way, so being a genuinely balanced full-stack dev is probably not the most realistic of goals — but staying open-minded is. While it is perfectly valid to dislike a particular technology, such as CSS, the industry’s culture of contempt for frontend work feeds into the outdated divide between frontend and backend, and detracts from building fast-moving, competitive teams and companies.
Think of yourself as a developer first. Investigate frontend technologies, pair with UI specialists, evangelize your colleagues. Your team, your company, and your users will thank you.
Thanks to Nathan Zeplowitz, Luke Belliveau, and Robin Weston for their excellent constructive feedback.
How to Be a Junior Software Developer
In my time as a junior at ThoughtWorks, I was blessed to have opportunities to grow and develop my skills on several interesting projects, with the help of some amazing people. In this article, I want to share the strategy I used to maximize my professional development. If you’re in a similar position, I hope this will help you make your first steps into tech successful. If you’re a senior or team lead, I hope that this will help you to support juniors in growing and becoming productive. If you’re a company, I hope that this will provide some context for how to hire and staff junior developers.
Learning to learn
I think it’s important to realize that, as a junior, your main value to an employer is most likely your potential, not your current ability. ThoughtWorks certainly didn’t hire me because they thought my technical interview solution should be pushed to production anywhere. So rather than focusing on delivering software 100% of the time, focus on up-skilling. Practice self-reflection and seek feedback to identify your weaknesses. Use whatever resources are available to you to improve your knowledge and skill set. Ask for help from seniors, ask for code reviews, pair program. Understand the context in which you are working - talk to business analysts, QAs, stakeholders, ask questions. Don’t stop moving, cultivate a humble ‘always learning’ attitude. Gain perspective on alternative ways to do things.
Learning itself is a skill that can be developed. I did not join ThoughtWorks with a particularly broad range of technical skills, and so, every project I joined brought a panic-inducing flood of new programming languages, new tools, new frameworks, and new concepts crashing down on me (and that’s not even mentioning business-related skills). These are the moments when impostor syndrome is at its strongest and undermines your self-belief the most. After a while, though, you begin to spot the pattern. It goes something like this:
- Superficial comprehension
- Frustration, having your expectations violated
- Working knowledge, productivity
- Rinse and repeat
Stage 3 is the most dangerous since in it you’re unaware of your own failure to understand what you’re doing. Better to be frustrated and proactive in seeking help than to cheerfully cook up vats of spaghetti code without knowing any better. Ironically, one can be quite ‘productive’ in stage 3. Get out of stage 3 by seeking feedback on your work and developing a relationship with your ignorance. Embrace it as a temporary given.
Once you’ve gone through this cycle a few times and ended up able to ship respectable production code in a new technology, you develop a ‘can do’ attitude towards learning. You become confident in your ability to pick up new things quickly and start to enjoy it. And, more importantly, you can make a plausible, evidence-based case for this to business stakeholders when needed. I have found that even senior developers sometimes end up lacking this panache because their roles have been too one-sided and have not required them to stretch. So, continuously extract yourself from your comfort zone and put yourself in the situation of having to learn new things - this experience will become your primary advantage as a junior developer, particularly if you work as a consultant. Set the tone for the rest of your career.
Adding value as a junior
Learning is all very well, but at some point, you need to get into the grind and rhythm of delivering software. Apart from your daily work, which will probably involve small and de-risked chunks of responsibility, I strongly recommend that you seek out independent areas in which you can add value to your team and turn them into pet projects.
For example, on my first project, we used C# in the back and React + Redux in the front. Since I hadn’t used any of these before, the barrier to entry to productivity was fairly high. I was lucky to have a tech lead who understood the importance of delegating other useful tasks to me, to keep me confident and productive while learning to use our core delivery stack. In this case, we were using an incomplete open-source build monitor tool for Visual Studio Online, so it became my task to finish implementing it. For context, I had no idea how build pipelines worked, and as it turned out, the monitor was written in Clojure, which has a paradigm that is hilariously different to the object-orientation I was used to. So for a month, I spent my nights producing horribly imperative Clojure code and watching talks by Rich Hickey. By the end, our team had a working build monitor and I had discovered my love of functional programming.
In both cases, I needed time to learn and become productive. By identifying things I could do, I was nevertheless able to start delivering genuine value to my teams quickly. These pet projects helped me build credibility and trust with my team and stakeholders, improved my confidence and reduced impostor syndrome, and allowed me to experiment with taking the lead on something (which is a whole world of learning unto itself).
There is always some extra utility to produce or something to refactor. While pursuing pet projects is particularly helpful for your development and team relationships as a junior, it’s a habit that I’ll continue to cultivate. It’s just so fun!
Choosing pet projects
Sadly, there’s no such thing as a free lunch. Like everything, the utility of pet projects comes at a cost. You may be uncertain or confused about where to start. You may not feel supported, or you may feel pressured to deliver functionality. You might be worried about the extra time, work, and effort required.
From experience, I can confirm that those are all real and valid challenges. In addition, expect to be continually frustrated and have your expectations of how things are supposed to work continually violated. To ameliorate these issues, I suggest that you pick projects that:
- Are small
- Have clear goals (definitions of ‘done’)
- Add value/will be appreciated
- Are likely to be supported once you advertise them (e.g. by team, company, community)
- Are fun/rewarding in their own right
Moreover, embrace both failure and ignorance. Suspend your disbelief that it will work tomorrow. Suspend your disbelief that you will understand it tomorrow.
And, of course, give yourself time off as well. Taking on pet projects is helpful and fun, but the expectation certainly shouldn’t be that you have to put in extra work beyond what you’re paid to do. Choose to do so when it makes sense for you, and when you have the space for it in your life. You can also speak to your team to see if they’ll allow you time to work on pet projects within work hours, which is definitely possible if there’s a clear value-add.
One of the key tenets of the Agile Manifesto is to prefer ‘responding to change over following a plan’. Continuous Improvement, Continuous Delivery. Produce a version of the product, observe its shortcomings, collect feedback, reassess requirements, produce the next version. Iterate in small chunks, never impose a grand vision based on outdated information. As a junior developer, it’s useful to think of yourself in the same way.
My Experience at Nordic.js 2017
I was up in Stockholm last week for Nordic.js and I figured I’d share some of my takeaways. I didn’t go a great job of taking notes, but I did come away with some strong impressions.
The conference was co-hosted by rising star Mattias Petter Johansson of FunFunFunction, which was cool since his show is awesome. The most bizarre moment of the conference came when MPJ randomly decided to get a tattoo and livestream it to the screen instead of appearing on stage towards the end of the second day. Talk about commitment…
There seemed to be a strong focus on diversity and equality on the part of the organisers. Talks were extremely well balanced, both in the gender of the presenters (it felt very close to parity), and in terms of the tech/community balance of topics. The second day also featured an optional “equality pep talk” breakfast to kick off the day. Even the color scheme of the event and the design of the venue seemed to be aiming for gender inclusivity - the palette was dominated by pastel shades and pink was very much in evidence. Of course, the audience was still mostly white dudes by a pretty huge margin, which had some interesting consequences (see notes on Karolina Szczur’s talk below).
Here are my notes on my favourite talks:
The Hilarious Misadventures of Being a Platform Downstream from Your Language
Myles Borins (TC39 Delegate & Google Developer Advocate for Node.js)
Borins is an interesting guy. He has a degree in art and another in music, and has been contributing to OSS on the side forever. He somehow ended up getting hired by IBM to work on Node full-time, and now he’s doing the same thing for Google instead. He was also on Node’s Technical Steering Committee for a while before stepping down in protest in due to the committee’s inaction in a recent code of conduct controversy.
In this talk, Borins described his involvement with the ECMAScript Technical Committee (TC39). The TC39 is comprised of various interest groups, including all the major browser vendors and technical experts (such as Borins, representing Node). They meet once a month in various locations, and it takes a minimum of four meetings to get a new feature approved. You need a full spec, an implementation in a major browser, as well as total committee consensus to become part of the standard - this is why it takes so long for new features to enter the language. Going to these meetings is expensive (voting members pay USD 70,000 per year) and essentially a full-time job for feature champions, who have to take on the role of developer, manager, and diplomat all at once. This bars most JS community members from impacting ECMA directly, but you can assist the champion for the features you care most about, e.g. by writing tests, working on the spec, etc.
Best Practices for Reusable UI Components
Mars Jullian (Netflix)
Netflix is apparently betting heavily on UI component libraries that can be shared and reused between teams. Jullian had a key role in developing their approach to this, and she kindly shared her key takeaways:
- Use React for your libraries. It’s easier to repurpose for other frameworks than vice-versa.
- Your component should physically begin at its visible edge (i.e. no margin or border, potentially no padding)
- Enough local state to work an an independent unit
- Tightest possible type-checking on props (including object/array shapes)
- Fewer props - make the component easy to understand and reduce cognitive load. Use smart rendering instead of extra props - e.g. don’t render a list if the prop comes in as
nullor empty instead of passing an additional
- Pass specific props to children; don’t blindly pass on all props (since this would allow unintended behaviour to be applied to children from the outside)
Reactive Web Animations with RxJS
David Khourshid (Microsoft)
As a functional programming buff and ReactiveX enthusiast, this was awesome. Not only does Khourshid have wicked UI skills, his reactive model of animations really makes a lot of sense. He demoed an observable model of animation-triggering events using RxJS, which could then have all the usual reactive transformations applied to them, such as merging, mapping, filtering, before yielding the desired visual effect.
The coolest thing? With this approach you can unit test your animations as pure functions!
Beyond the Bubble
Ben Schwarz (Calibre)
We need to stop optimising for $3000 MacBooks. We set out to build the world wide web, not the wealthy Western web.
While we think of performance as less and less relevant these days, this is a very biased and unrealistic point of view. There are tens of millions of first-time internet users entering the market every year in India alone, with the vast majority being mobile-only. These users do not typically enjoy the latest/most performant devices, nor the fastest connections, nor the cheapest data plans. So slow/large applications can be crippling from both a usability and financial perspective.
The most critical information, such as main text content, should be available instantly, independent of styling and interactivity status (e.g. if you visit The Guardian’s site with JS turned off, you should still be able to read the news). According to Schwarz we need to be paying particular attention to load order here, particularly with webfonts, which get loaded lazily (only when text tries to be rendered that wants to use that font). Single page apps, which require all JS to have been downloaded, processed, and run in order for text to be rendered, can cause quite a delay for a webfont to be loaded and text to be displayed.
In these cases, we can play around with load order using the preload tag (and I suppose you could come up with some server side pre-rendering solution, too). In any case, “time to first paint” and “time to interactive” should not be the same thing. We can keep ourselves honest by doing regular, realist perf audits using the the excellent Chrome devtools and mediocre (realistic) devices.
Who Cares Why Undefined Is Not a Function
Tereza Sokol (NoRedInk)
Can essentially be summarised as “Elm’s pure functional approach and solid type checking gives you back the time you waste looking for the cause of unhelpful JS error messages”.
Harriet Lawrence (Buildkite)
Coming from a dev background, Lawrence is a technical writer at Buildkite, a CI tool maker based out of Melbourne, Australia. She’s also currently getting her Master’s in Sociolinguistics, focusing on tech communities, so she’s got a real passion for and deep knowledge of the intersection between language and culture, particularly in the tech space.
I would summarise her message as “words matter”. The way we talk about our work and communities in public forums (e.g. Github) can really make a difference in how likely outsiders are to join and feel empowered/valued, and how likely existing members are to continue contributing. In addition to obviously not being an asshole, Lawrence suggests that we can create productive environments by creating a community that accepts weakness (e.g. by tweeting something like “this thing was really hard and I thought I’d never get it but hey, looks like I’ve learned something”) and that recognises effort and progress (e.g. merging a PR from a new contributor by saying “nice use of technique X, you’re really getting the hang of this tech stack” rather than “cool, merged”).
Chatting with her afterwards, I asked her about what to do when someone just refuses to accept that their behaviour/language matters or has an impact on the community, since this seems to happen a lot (see Node’s recent code of conduct debacle for an example). She told me that there are some people who will just never be persuaded and have that empathy, and that these people will sadly keep creating a toxic environment for others. In those cases, Lawrence says that nowadays she just stops working with the individuals in question.
Building Inclusive Communities
Karolina Szczur (Developer, designer & conference organizer)
This was a very well-done intro to privilege, diversity, and inclusion. While I personally didn’t hear anything particularly new here, it appeared that the topic was new and uncomfortable to quite a few audience members. A number of people got up and left during this presentation, and while I can’t say for sure, it did seem to be only men who did this. Afterwards, Szczur tweeted an email she received from a male conference attendee, saying that he felt discriminated against, remarking that “this is not Trump’s America”, and claiming that “we don’t have [gender inequality] in Sweden”.
Clearly we still have quite a way to go, but well done to Karolina for meeting the exhausting task of educating.
Migrating from Reflux to Redux
Because Reflux and Redux both follow the basic Flux architecture pattern of unidirectional data flow, we can incrementally take over the Reflux state cycle with Redux:
- Complete Reflux cycle
- Action handling (e.g. async) in Reflux, but keep all state in Redux. Reflux store action listeners dispatch Redux actions with the results of remote API calls and no longer call
this.trigger. React components receive props mapped to Redux state and no longer listen to Reflux stores. At this point, Reflux essentially acts as async middleware for Redux.
- Action handling (e.g. async) in Redux, with appropriate middleware. Reflux store action listeners now do nothing but straight away dispatch corresponding Redux actions.
- Complete Redux cycle – stop dispatching Reflux actions from React components and start directly dispatching Redux actions.
Thus, you can break the refactor up into bite-sized chunks and spread it out over time if required (e.g. due to other high-priority work/new scope coming in), keeping your app fully functional at every stage.