Transcript
This transcript was autogenerated. To make changes, submit a PR.
Jamaica real
time feedback into the behavior of your distributed systems and
observing changes exceptions. Errors in real time
allows you to not only experiment with confidence, but respond
instantly to get things working again.
Close hello,
my fellow javascript. My name's James,
I'm a programmer, I work with my family,
we make apps for horse riders.
My sister's the designer, my dad's the Sysadmin,
I'm the programmer and my mum's the boss.
Now, I'm going to be talking today about the feedback loops
that we experience as programmers.
So I reckon this is a pretty typical
feedback loop for a web developer today. So you
might start off changing some code, then you save the file
you're working on, rebuild your application.
If you're lucky, only the part of the application you've been working on will get
rebuilt because that'll be faster. Reload the page
because you need to be working on the latest code, right?
And again, you might be lucky, it might only reload
the portion of the page that you've been working on. And then
you need to have a test, have a poke around, see if what you did
worked. You might run some automated tests as well.
And then you have to figure out if things didn't go well,
what mistake did you make? And there'll be a period of debugging
and that will usually lead you back to changing more code.
Now the steps here can pretty easily be divided into doing steps
on the left and waiting steps on the right. So the build
and the reload are waiting steps. And when you start
a brand new project, you don't even notice the waiting steps.
They finish so fast, right? But that doesn't
last because as your application grows,
as you add more work to the build chain,
you do start seeing delays. At first they're just subsecut
delays, but they can get longer and longer. And in
worst case scenario, you can end up spending more time waiting than
doing.
So why is slow feedback a problem? Isn't it nice to have a break,
relax our mind while we work? Well, I don't think so.
I think feedback should be fast for three reasons.
One, programmers are creative people,
and like artists, we respond really badly to tedium
and boredom, right? So a slow testing loop,
a slow testing step, a slow build step, breaks our concentration
and takes us out of the zone. And the zone is where we want to
be, right? That's where we do our best work. Two,
it can be really difficult to predict the consequences of
changing a piece of code. And that's
why it's safer to make our changes in small increments and get feedback
each time. A slow feedback loop encourages
us to make a lot of changes all at once and
then see if what we did worked. And what that
does is obscures our mistakes because we've touched a whole lot of
the code base and it's no longer obvious where we've made our mistakes anymore.
And that just leads to more time debugging. And it's really the worst kind
of debugging because no one's going to thank you for fixing a bug
that you created 15 minutes ago, are they?
Lastly, we want to finish our day with a sense of accomplishment,
and it's hard to feel like that if you spent a lot of your day
just waiting around. Now I'm
going to show you a way to get really fast feedback in Javascript,
but first I'm going to tell you a tale.
Before the 80s, before the microcomputer revolution,
before everyone had personal computers,
computers were big, sometimes taking up a whole floor
of an office. They were slow, maybe thousands,
million times slower than computers today.
They could only really do one thing at a time. And they were astronomically
expensive. Because they were so expensive,
the organizations that ran these computers and paid
for them were very reluctant to let them sit idle
ever right. So the name of the game was keeping
utilization at 100% as much as possible.
Like keep those computers burning 24 hours a day, seven days a week
to try and get your money's worth. And the way that they achieved this
was through this thing called batch processing, where you would
create a batch job out of several different programs instead
of running them separately. So you might get, say ten programs
and stitch them together like first program,
second program, third program, fourth program, and feed
that batch job into the computer. The computer would churn
away work at the first job, spit out the output, move on to the next
one, burn away at that. And when all of the batch job was
finished, a human operator would come in and they would
put in the next batch. And this cut down on handler
time. Operator Handler time was very efficient.
The people that paid for the computers loved
batch processing because they really felt like they were getting their best
bang for their buck. But guess who didn't
love batch processing?
The programmers. Us.
The reason it sucked for programmers is that you had
to compete for computer time
with all of these other jobs. So like there might be a
payroll job that calculated everyone's pay for
the company, they might be crunching some numbers, and you just
want to debug your program, you just want to run it to test it,
and you have to submit it along with this batch job,
wait hours, possibly days. It wasn't unusual to have
a 48 hours turnaround for batch jobs,
so made it a real pain to test
your code. Now we're not doing batch processing
much anymore, or barely at all. So what happened?
Well, pretty early on, there was a synergy forming between
computing and telegraphy. So telegraphy
is the long distance transmission of textual messages by
electronic means, right? So nothing
fancy, just text messaging, like we do all day, every day now.
And the way telegraphy used to work is you would stroll into your telegraph
office, and you would pay some money to
send a short text message, and they would
take your text message and hand it to an operator sitting
at a teleprinter. A teleprinter,
as you can see in this photo, is sort of. It is what it
looks like. It's an electric typewriter hooked up to a
phone line, and the operator would dial up some
other operator somewhere in the world who also had a teleprinter.
And when they connected, your operator would type out your message,
which would be printed but in front of the other operator,
and they would slice off your message, and it would be hand delivered to
the recipient. Now, these machines were used
for a long time for human to human communication like this,
way before computers. But in 1955,
someone had the bright idea to hook one of these computers up,
to hook one of these teleprinters up
to a computer. And that's where we got the first command line interface.
Now, one programmer who saw the potential of this interface
was John McCarthy, and he had this
incredibly visionary thing to say in 1959.
Suppose that the programmers has a keyboard at the computer.
So the programmer, not the computer operator,
the programmer. Then they can try their
program, interrogate individual pieces of data or
code, to find an error, make a change, and try
again. So what he's describing here is
basically interactive programming. So programming
with a fast feedback loop where you can just
really delve into what your code's doing.
The only problem with this vision was that at the
time, 1959, no one could afford to use computers
like this, because recall that computers
were very, very expensive, and they could only do one thing at a
time. And if that one thing was interactively
programming, that was a huge waste of that computer's time.
Because if you think about it, when you program, you spend most of the
time thinking. You spend a bit of time typing,
and you spend barely any time at all actually running your program.
So, in effect, that mainframe would have just been
sitting there idle the whole time.
John McCarthy's genius idea
was that if you had enough programmers all working
on a computer at the same time, then that time you spent thinking or
typing would cancel that with someone else running their code,
and you could end up having a reasonably high utilization of
your computer. And John McCarthy called this idea timesharing.
And his team developed the first timesharing system.
So essentially the first computer that could do loops of things at the same time.
And he did that in 1962, and it was big hit with
universities and stuff. But since then, the idea
of timesharing has exploded. The Internet is timesharing.
The idea of having a server that talks to multiple clients
at the same time is a form of timesharing. If you've heard of AWS
Lambda functions, that's just timesharing.
Amazon's just trying to get the most out of their hardware there.
So another great invention that John McCarthy had was the Lisp
programming language. John McCarthy was
an artificial intelligence pioneer. He was one of
the first people working on AI, and that means he has in the
business of writing smart programs. And they
were having trouble doing that with the languages they had at the time, right?
So they had Fortran, which was basically for
scientific number crunching. They has COBOL,
which was a sort of business money oriented language
that is good at producing reports and stuff,
but they were struggling to model intelligence in these sort
of clunky old languages. So John
McCarthy came up with Lisp, which is a much higher
level language than those other ones,
and it has a whole bunch of features that we totally
take for granted. A language missing any of these features
is pretty handicapped these days. And so the
features that appeared in Lisp for the first time
in a high level language were conditionals. So like
if else also in JavaScript we had
the ternary operator. That's a kind of conditional. You don't
even want to know what they were doing before conditionals recursion.
So a function can call itself by name first
class functions. So functions can receive other
functions as arguments, and we use that all the time in
JavaScript. When you register a callback or register an event listener,
the fact that you're passing a function in has an argument that makes it
a first class function, a listpad garbage collection,
which was really cool, really neat idea. Instead of the programmer having
to manually manage their memory and remember to release memory
here and there, when they were no longer using it, the computer just looked
after that. And that freed up these Lisp
programmers to focus on what they were actually trying to do,
which is model intelligence. And lastly,
Lisp had the repl, and I'm going to talk a bit more about
the repl, but I just want to point out that JavaScript
has all five of these features, so anything that's
true for Lisp is essentially true for JavaScript.
So the repl is named after
the functions in this very short lisp program.
So if you're not familiar with Lisp, each matching
pair of parentheses is a function call,
and I've color coded them here to make it a bit easy to read.
So let's walk through what this program does. The first
thing that happens is the read function is called with no
arguments, and that sends a prompt to the user's teleprinter
and waits for them to type in some source code. When the user's done,
they press return and read returns that source code
to eval. Now, eval evaluates
the source code, it runs the source code, and it returns the
resulting value. That value is passed
to print, which prints that value out textually
to the teleprinter. And when print is done,
loop sends us back to read,
read eval, print loop
repl the best way to understand how the repl works
is to see it in action. And as Javascripters we are blessed
with a proliference of repls. In fact,
every modern browser has a repl in it, and it's super easy to
bring it up. I'll just do it right here. I'm just going to right
click anywhere on the page,
press inspect, and we get
the devtools. Now I'm in chrome here, but it's
a very similar way if you're on Firefox or Safari,
and we want the console tab. That's where your logs and errors get spat out.
And down the bottom, beneath all your logs and errors,
there's a prompt, and that's where you can type in your source
code to be evaluated.
Here we go. We can also evaluate functions,
and we can evaluate functions that have side
effects as well. So we'll do a little
hello world. Notice we got two results
here. We had the hello world being printed. That was the first thing,
but then we get undefined, and the reason we get undefined
is that hello world was the side effect. But console
log returns undefined. The thing about the repl is you
always get some value back, even if it's just undefined.
And we're hooked up to the page here so we can access
the Dom. So I'm going to use the repl
to give this slide a little
bit of historical accuracy,
because after all, we didn't get lowercase until the late 60s.
Because we're interacting with a living running page, we can also
schedule tasks for future execution.
So if I pass set timeout, a function,
in this case the log function,
we're going to ask for log to be called with
a string in 5 seconds.
Set timeout returns a timer id, which we
can use to cancel the job, and then it prints out our string.
So the great thing about the oracle, great thing about the repl
sorry, is it's a form of oracle, so we ask it questions
and it gives us answers. And that's really handy for a language
like JavaScript, because, well, I mean,
it's no secret that Javascript was designed
in ten days, didn't get everything right, actually got a lot of things wrong,
and it's vital having a way to sort of check out
language features before you go and put them in your program, where they might cause
horrible, hard to debug problems. For instance,
JavaScript has this y two k, but I'll
show you it. So we're going to ask the current date
what its year is,
and we get back just a completely nonsensical
answer. So what this method was intended to do
was give us a two digit year, but what it does in
reality, and this is what the JavaScript specification
says it does, is it returns the number of years since
1900, which is insane.
JavaScript was designed in 1995,
so that meant the date function was going to work for
five years. Interestingly, it's not actually JavaScript's
fault as such. If you look at Java,
Java has a date object, which has a get year method,
which does exactly this. And the only reason this is in JavaScript is because
Java did it first, and JavaScript had to copy some superficial
things from Java. Right now,
everything, all the code I've typed in so far has been very low level,
and that's because all we've really got to work with here are features of the
language, like numbers, math,
random, and features of the runtime, like the
document object and anything that we've
declared via the prompt. So I could declare a variable or define
a function and then call that later on.
And that might have been all right. It might have been all right to just
have this low level kind of repl in the 60s
when people were still writing their programs on paper, but it's
not really compatible with how we write applications today.
So these days we split up our application
into modules, and we store these modules
in files and we edit them with a graphical
text editor we're not using teleprinters anymore
and it's not entirely obvious how we're supposed to sort
of get our own code, our own modules or our third
party modules into the repl so that we can play around with them.
One thing you might think of doing is copying and pasting stuff into the prompt,
and that will sometimes work. It might work for small things, but if you
try to paste a whole module in there,
it's going to blow up. You should not allowed to have an
import statement or an export statement in the
repl in this repl anyway.
I was quite surprised to learn a couple of years ago that this
problem has been solved for almost 50 years,
right? So when Lisp programmers first
got computers with screens,
they realized that they weren't doing this teleprinter thing anymore and they adapted.
And I want to show you what their solution looks like when applied
to JavaScript. So this is
a garden variety text editor vs. Code.
And I've got a plugin installed so
that when I press in keyboard shortcuts,
source code gets sent to a repl in the background and returns
back here. So don't worry.
But that webull lost for now,
so we can do all the old favorites, we can log,
we can even blow up if we want. There we
go, error, boom. And because we're still hooked
up to a browser, we still have access to the document object.
Let's run that. So we just modified the
document there so we
can do everything the other repl could do, but with more convenience
because we're in our text editor where we actually write our code,
but we can actually go one step further.
This repl supports modules,
so I'm going to report view, which is the Javascript
library for doing user interfaces.
There we go, we'll import that now, we'll see
what we got, we'll see what that version member holds. There we go,
we're working with view 3.2.30
been. So this is pretty epic.
All of a sudden we can just start importing modules and playing with them
as if they're low level features of the language, which is awesome.
One thing that we can do with this new fanpower is use the
repl to write a module.
So I'm going to write a component using the repl.
This is called RePL driven development.
First thing I write when I start writing a module is a specification
just in, just in plain English.
So in view, a prop is like a parameter for the
component,
right? So now I've got a specification it's
good to write some kind of test. It's good to write the test before
the implementation because then you get to actually test the test.
Now there's two ways of testing
UI components. You can either test automatically or
you can test manually. When you test automatically, you have
to simulate a human, which I think is really hard. So what I'm
doing instead of automated UI testing these days is just
for every component I write, I write a demo. And that
makes it super easy to play around with the component,
debug it, see how it works. So this
is how I'd write a demo for view component.
Now, we haven't ridden our polite component yet, so that's
obviously going to fail when I evaluate it.
And just going to mount this single component
app to the body element. All right. Now to
run this demo, all I do is select
the entire module and evaluate it. That fails
with a reference error because polite component is not defined.
But we can fix that by implementing the component.
So a view component consists of a props property.
That's where we list loops and
a setup function. Now the setup function
is called whenever a new instance of the component is instantiated.
And this is where we can do all our one time setup work, but we
don't actually have any to do right now. So we're just going to go ahead
and return a render function.
So the render function is essentially called
every time the props change. And it's responsible for returning
a tree of dom elements to be rendered on the page.
And the function we use to make a Dom
element is the h function.
And we're just going to create a button element and
we're going to put the pleasantry text inside the element.
All right, so let's run the demo.
There we go. We've got a button,
but it doesn't do anything. And our specification says
that it's supposed to respond to pleasantries, not just invite them.
So we need to do a bit of work still.
We're just going to add an event handler to our
button it.
All right, so let's run that again. There we go.
There we go. Polite little
component there. All we got
left to do is export the component.
And I'm also going to comment out this demo,
but I'm not going to get rid of it because it's going to be useful
the next time I come back to work on this module. So I'm going to
keep it around.
Now, what we end up with here is something that I
like to call a whole module because this module contains its
specification it contains its best and it contains
its implementation. And this is actually a very powerful idea.
It gives you better portability. You can pass your modules around
to different projects, they don't lose anything.
It gives you better maintainability. Like how often
have you found your way to a source file in an unfamiliar code
base and you don't know where that file's tests
are, you don't know where its specification is. This is
good, because when you end up at that buggy line of source
code, then you've got your specification and your best right there. You can just
immediately start fixing things.
Another thing that's neat about using this whole module
repl approach is that you get a better feedback loops.
So this is what our feedback loop looks like. Now notice that the
build and reload steps are gone, and instead of saving the
file, we evaluate code.
The test step is going to take less time because
we're not running tests for anything else
except this particular module. So we've just got less code to run there.
Debugging is going to take less time because
we're not running the whole application, we're only running our module,
which means that we're not going to have to worry about bugs that exist
elsewhere. So it's going to make it easier to track down our mistakes.
The repl I've just been using is called replete
and I wrote replete because I'd heard about people using
the repl like this in other languages, but I'd never
heard of someone doing it in Javascript. I wanted to know if it was possible
and I wanted to know what it was like.
Now I can report back. I really
love it. I think it's really great having this super fast feedback.
I use replete for front end code, backend code,
crazy experiments, stuff I'm putting in production.
And you can use it too. It's fully open source, it's on GitHub.
You just have to get that source and then install a plugin.
There's plugins for sublime vs code,
neo vim and emacs. The plugin
actually doesn't do much. All it does is listen for keyboard shortcuts
and send messages to replete. So like the plugin
for vs code is like 60 lines of javascript. So you
could write that in an afternoon.
Once you got your plugin installed, you can start evaluating your
code in any browser, to any modern browser,
even a browser running on your phone or something. You can evaluate code
in node and you can evaluate it in Dino,
which I feel is the spiritual successor to node.
So just to go back to this comic,
like I love XkCD, but this
comic kind of depresses me because it reminds me of how
I feel when I'm waiting for a test step or a build step,
and I'm not having sword fights. Like, I feel restless,
I feel trapped because I want to continue on with
what I'm doing. I don't want to move my focus onto something else.
But I can't progress until I get that feedback, and it drives
me nuts. And it's a
waste of time, right? So programmer time
is now the most precious resource in any software
project, so we just can't afford to waste it.
Good news is the computers are really fast now. They can do loops
of things at the same time, and if we make it a priority,
we can devise feedback loops that have no
noticeable delay, right? So feedback loops that feel like
that brand new project, but they keep
feeling like that no matter how big the project gets.
And I think that's in the spirit of what John McCarthy
was talking about in the we
can have that now, especially with Javascript.
Thanks everybody. Thanks. If you made it through that,
I found these two things really cool. What makes Repl is a
podcast episode that just really explains how
to do repl driven development. The difference between poking around
on the node repl and really getting it integrated into your text
editor. The second thing is a video that
goes a bit more in depth. I love talking about
this stuff, so feel free to email me.
I'll talk about repls all day. I've got a blog, I've got
a GitHub. Have a great 42.
Thanks so much everybody. See you later.