Mar 29, 2018

Dividing Frontend From Backend Is An Antipattern

Dividing Frontend from Backend is an Antipattern

This article was originally published on ThoughtWorks Insights.

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.

And yet, I come across the frontend/backend mindset regularly, even at the most technologically progressive companies. After spending a year introducing modern JavaScript on my previous project, I was told to stop focusing on frontend because “we’re application developers”. Every developer I’ve ever met who has exceptional ability in client-side work has similar stories to tell. This dismissive attitude speaks both of a failure to understand how much JavaScript and the whole frontend ecosystem have evolved in recent years, and of a close-mindedness that makes it hard for our teams to adapt and deliver software to the expectations of the modern market.

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.

Here’s the reality: JavaScript has absolutely exploded in the past few years. Nowadays, with the advent of ever more complex client-side applications (think responsive, immersive, offline-enabled experiences), significant chunks of functionality are moving from the backend to the frontend. We now think about the same problems in the front as in the back. The issues I listed earlier (testability, maintainability, persistence, asynchronicity, state, API design) are now the daily bread of client-side developers.

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

Client-side engineering is a serious business need. JavaScript is taking over the world, and we can’t afford excellent and experienced devs thinking of User Interface (UI) and User Experience (UX) work as outside of their area of concern. At ThoughtWorks, we are seeing more and more engagements that involve a revamp or total rewrite of our client’s browser and mobile applications to align with the needs of modern consumers. In other words, actively building and maintaining client-side capability is critical to surviving in today’s market — not to mention tomorrow’s.

You might think that the correct solution here is to simply hire more engineers into traditional ‘UI Developer’ roles. But it’s not, for two reasons. Firstly, recruiting and retaining experts is famously challenging in the tech industry. Thus, our focus should instead be on enabling our existing workforces in frontend development — particularly given the similarity of modern frontend and backend work. Secondly, having dedicated UI Developers promotes problematic team dynamics. How many of us have been on teams where all or most frontend architecture and development was relegated to “that JavaScript person”? This can lead to information silos, less solid testing, and an overall reduction in code quality, which in turn poses a serious business risk.

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

Ironically, the crown jewel of backend quality is and always has been user interface design. Every design pattern ever invented, every book on architecture ever written, every DRY refactoring ever performed: all tools to help the authors of code enhance the productivity and happiness of the readers of code. Whether you’re creating an inheritance hierarchy, an API, or an ecosystem of microservices, you’re creating it for someone. If you’d be embarrassed to release an un-RESTful API or a class with leaky abstractions, then you care about user interfaces. So we already think of software quality in terms of user experience — it’s just that the users happen to be other developers.

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”.

Stay open-minded

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.

Sep 20, 2017

How To Be A Junior Software Developer

How to Be a Junior Software Developer

This article was originally published on ThoughtWorks Insights.

Introduction

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:

  1. Enthusiasm
  2. Bafflement
  3. Superficial comprehension
  4. Frustration, having your expectations violated
  5. Working knowledge, productivity
  6. 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.

My next project was working on a Java microservices platform using Docker, Kubernetes, and Cassandra. Once again, a new tech stack. This time, I bridged the time needed to get ramped up on the new tools and concepts by making myself useful in the UI. I used my previous experience to help advocate for and execute a migration of critical business flows from Reflux to Redux. This increased the testability of our JavaScript and reduced the complexity of our inherited codebase, which in turn resulted in improved velocity and stability.

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.

Conclusion

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.

Keep learning.
#juniordevforlife

Sep 13, 2017

My Experience At Nordic.js 2017

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.

MPJ gets a tattoo

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 null or empty instead of passing an additional shouldRenderList prop
  • 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”.


Sociolinguistics and the Javascript Community: A Love Story

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.

May 5, 2017

Migrating From Reflux To Redux

Migrating from Reflux to Redux

I recently helped my team move a large codebase from Reflux to Redux (I have written exensively about my experience on ThoughtWorks’ Insights blog). Both are JavaScript-based state-management tools. This was an interesting technical challenge, and a fun one.

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:

  1. Complete Reflux cycle
  2. 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.
  3. Action handling (e.g. async) in Redux, with appropriate middleware. Reflux store action listeners now do nothing but straight away dispatch corresponding Redux actions.
  4. Complete Redux cycle – stop dispatching Reflux actions from React components and start directly dispatching Redux actions.

Redux Migration - Technical Diagram

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.

Feb 15, 2017

Code Sharing: Classes Vs Prototypes

Code Sharing: Classes vs. Prototypes

I originally wrote this article for students at Code First: Girls London. You may find it useful if you are interested in prototypal inheritance and JavaScript in general, and have some experience with classial inheritance. The article assumes some Ruby knowledge, but experience in any classical language (Java, C#, etc.) should translate well.

Introduction

On your coding adventures so far, you’ve probably already realized that in order to maintain and organize your programs, you need to introduce some sort of predictable and intuitive structure. A common problem is sharing data or functionality between separate parts of the program that need to do similar kinds of things.

One way of achieving this is with the concept of inheritance. You can think of it in the same way as biological inheritance – you inherit some attributes from your parents, but might still have attributes of your own that your parents don’t have. The most common form of sharing is called classical inheritance (classical as in “involving classes”). Lots of people feel that this works well in object-oriented languages (Rich Hickey would disagree!), and you’re probably already a little familiar with it from Ruby or another classical OO language.

But beware! Javascript has neither classes nor classical inheritance, but it’s often written as though it did! In fact, the language makes several provisions to help programmers “pretend” that they’re writing classes, which can be pretty confusing. For example, ECMAScript 6 (the 2015 version of JS) implements a class keyword. Wat?!

This has historically been because JS was often written by people who spend most of their time programming in other, likely classical, tech stacks (less so nowawdays because you can now write fullstack JS). So it was sensible for the language to provide familiar-feeling solutions to those developers. However, JS has its own way of sharing data and functionality: prototypal inheritance.

In this article we are going to learn about this very simple and powerful concept. Hopefully it will clear up any misconceptions you might have about JS and help you avoid future confusion.

Classes in Ruby

Let’s use classes in Ruby as a refresher on how classical inheritance can work. Say we’re bored on weekends and want a loving puppy to keep us company. We could write:

class Dog
    def initialize name
        @name = name
        puts "Why hello there. I am your new dog, and my name is #{@name}!"
    end

    def bark
        puts "WOOF!"
    end
end

This creates a “blueprint” for a type of thing that we’ve called Dog. When you ask for a new Dog, Ruby looks at the blueprint and gives you back an object that has all the methods and properties that we’ve specified in the class. This is called instantiation – making a specific thing (object) based on an abstract thing (class). Of course, in Ruby, classes are objects in their own right (instances of the class Class), but let’s not go there…

The initialize method runs once when the object is brought into life, so it’s often used to pass properties needed to make a class instance fully functional (for example, in this case the name argument gets saved in the instance variable @name and could be accessed later if needed).

With me so far? Cool, so let’s go get our puppy…

spot = Dog.new("Spot")
# Why hello there. I am your new dog, and my name is Spot!

spot.bark
# WOOF!

Nice. So now we have a barking dog. But what if we want to go for walkies? Guess we should add some form of walking functionality…

class Dog
    def initialize name
        @name = name
        puts "Why hello there. I am your new dog, and my name is #{@name}!"
    end

    def bark
        puts "WOOF!"
    end

    def walk
        puts "Aw yiss, going for walkies!"
    end
end

spot = Dog.new("Spot")
# Why hello there. I am your new dog, and my name is Spot!

spot.walk
# Aw yiss, going for walkies!

That’s nice and all, but doesn’t it feel weird somehow to have walk associated with Dog? Surely walking is not a dog-specific thing. A more accurate domain model would be to think of a dog as an example of one type of thing that is able to walk – for simplicity’s sake, let’s call it Animal. Sounds like another class to me…

class Animal
    def walk
        puts "Aw yiss, going for walkies!"
    end
end

So now we have dogs and animals. To give Dog access to walk, we need to somehow connect the two classes. That’s where inheritance comes in.

class Animal
    def walk
        puts "Aw yiss, going for walkies!"
    end
end

class Dog < Animal
    def initialize name
        @name = name
        puts "Why hello there. I am your new dog, and my name is #{@name}!"
    end

    def bark
        puts "WOOF!"
    end
end

spot = Dog.new("Spot")
# Why hello there. I am your new dog, and my name is Spot!

spot.bark
# WOOF!

spot.walk
# Aw yiss, going for walkies!

Dogs are a logical subset of animals, yes? All dogs are animals, but not all animals are dogs. So, by representing that in our code like above we can include all Animal code in the Dog class. You can use spot.methods to get an array of all the available methods on our puppy, and you’ll see that it includes both bark and walk!

spot.methods.include? :bark
# true

spot.methods.include? :walk
# true

The beauty of this design is that we can now create as many other kinds of animals as we like, and they’ll all be able to walk! And there you have it, the basics of classical inheritance.

“Classes” in JS >= ES6

As mentioned before, there is now a class keyword in JS. So you can now write things like this:

class Dog {
    bark() {
        console.log("WOOF!");
    }
}

const spot = new Dog();
spot.bark();
// WOOF!

Hooray. We now have a JS blueprint for a barking dog. For a more realistic example, let’s look at Facebook’s React, which can be written with this kind of setup:

import React from 'react';

class ExampleComponent extends React.Component {
    constructor(props) {
        super(props);
        this.setState({
            message: "Hello, world!"
        });
    }

    render() {
        return (
           // render some JSX
        );
    }
}

The idea here is that when new ExampleComponent() gets called, constructor runs first (like initialize in Ruby). The call to super means that the constructor on the superordinated class (the class that ExampleComponent inherits from – in this example React.Component) will also be called. The same concept exists in Ruby, Java, and other classical languages.

All seems pretty familiar, doesn’t it? Still, it’s important to remember that this is not the same as class in Ruby. In fact, it’s just a thin layer of syntactic sugar around the way people used to write fake classes in JS back in the day, before ES6 was a thing. That means that when you write a class in ES6, what you actually get is…

“Classes” in JS < ES6

You might already be aware that Javascript has first-class functions – you can pass functions around like variables and then use them whenever you like. You can also define constructor functions that can be used with the new keyword (but you have to use the function keyword rather than using the new ES6 arrow functions).

const Dog = function() {};
new Dog();
// Dog {}

FYI, constructor function names are capitalised by convention.

Interestingly, JavaScript functions are just key-value collections. What I mean by that is that, in essence, they’re just like hashes in Ruby, dictionaries in Python, or maps in many other languages. You can assign properties to them and access their properties later.

There are, however, a few things that distinguish functions from normal JS objects. For one thing, they are callable. That means that when you define a function, you can equip it with a block of code that it will remember and be able to execute when you ask it to.

Another of the special properties they have is called prototype. At the moment, we haven’t set a prototype on Dog:

Dog.prototype;
// undefined

Let’s play around with it now to see what it does:

const bark = function() {
    console.log('WOOF!');
};

Dog.prototype.bark = bark;

Dog.prototype;
// { bark: () }

const spot = new Dog();
spot.bark();
// WOOF!

We define a bark function, then add it to the Dog prototype. And like magic, our new dog spot is able to bark.

The class keyword in ES6 works exactly like this, it just saves you the trouble of dealing with prototype. But it writes the exact same code for you at the end of the day, so that the ES6 Dog from the previous section and the one we’ve just written are completely identical.

Prototypal inheritance: The prototype chain

OK, so we’ve seen how classes work in Ruby, and we’ve seen a very similar-looking thing in JavaScript. So why do I keep saying there are no classes in JS? Time for a peek under the hood to see how inheritance in JS actually works!

Every object in JS has a property called __proto__. You can think of this as a kind of “phone book”, or a lexion – whenever you ask an object for a property that hasn’t been defined on it, it can pass the request on to the object defined as its __proto__. This is known as delegation.

In this way, all JavaScript objects are linked together in a cooperative lookup arrangement known as the prototype chain. (If you’re into computer science, this is conceptually pretty similar to a linked list). If you follow this chain for long enough, you will always end up reaching Object, the final link.

new Object().__proto__;
// Object {}

new Array().__proto__;
// []

new Array().__proto__.__proto__;
// Object {}

new Dog().__proto__;
// function() {}

new Dog().__proto__.__proto__;
// Object {}

It is indeed objects all the way down (forgive the joke).

To summarise, the major difference between prototypal and classical inheritance is that the functionality of superordinated classes always becomes part of inheriting classes, while prototypes are simply independent objects that get delegated to by other objects.

There are many neat tools in JS, such as Object.create(), that let you exploit this prototypal functionality in order to knit together your objects in useful and elegant ways that allow you to “share” (read: delegate) functionality without locking your domain model into rigid class hierarchies.

Prototypal inheritance: Constructor functions

But Rufus, I hear you cry, earlier you were talking about prototype, and now we’re suddenly talking about this bizarre thing with four underscores called __proto__. What gives?

No worries – this final mystery, too, shall be divulged.

It turns out that a function in JS essentially gets two prototypes – __proto__ because it’s just another kind of object, and prototype just in case you want to use it as a constructor function. Every time you “instantiate” your constructor funciton, JS actually gives you back a brand new object with its __proto__ set your constructor’s prototype.

function CodeFirstGirls() {};

CodeFirstGirls.prototype.isFun = true;
CodeFirstGirls.prototype.javaScriptRating = "over 9000";

CodeFirstGirls.prototype;
// {
//    isFun: true,
//    javaScriptRating: "over 9000"
// }

new CodeFirstGirls().__proto__;
// {
//    isFun: true,
//    javaScriptRating: "over 9000"
// }

As a final aside, an interesting thing in ES6 is the introduction of arrow functions. This is a more elegant and concise way of writing functions: () => {} as opposed to function() {}. However, you can’t use ES6 arrow functions as constructors because they don’t have their own this context. But that’s a whole other conversation for another day, so relax if that sounds like meaningless gibberish to you. You can read more about arrow funtions here.

Try it yourself

If you’re feeling comfortable with what we’ve covered here so far, you can try to re-implement JavaScript’s new. The function should be called renew and implement prototypal inheritance.

Hint: you might want to look into Object.create.

Further reading

If this was interesting to you, I recommend that you pick up JavaScript: The Good Parts, by Douglas Crockford. It is the classic text on writing good JS, and what’s more, it’s short, readable, and funny (as well as highly opinionated). It includes an excellent introduction to prototypal inheritance.

Previous | Next