Essentials • iOS, OS X • 53:26
All software projects start somewhere and evolve through the accumulation of your effort, changes in technology, bug fixing, and the addition of features you could not have foreseen when the project began. This session discusses this process and provides you with ideas which can help you build your software projects to last.
Speaker: Ken Kocienda
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good morning, everyone. Thank you for coming. So, this is session 212, basics plus habits, building your software projects to last. I'm Ken Kocienda, and again, thank you for coming. So, I think, right, when you think about building a great project, I mean, you kind of, it means for us, for this audience, it means building a great application, right? That's why you're at WWDC.
You're probably your app developers and you want to make great apps, right? And if you are successful in making an app, that means that your app is probably going to change. You're going to be shipping updates to that app as new features, new OS releases come out. There are going to be bug fixes, you know, updates, all of this kind of thing. So, success means your software is going to change.
You probably know that radical changes rarely work. If we take the example from biology, Darwinian evolution, most mutations kill the organism. You have a radical change, it doesn't work. I think the same holds true for software. So incremental change is better. And last year, how many of you saw my easy to change code talk last year perhaps? I guess a couple had security. Those are the people you probably want to leave. They're crazy enough to come to my talk two years in a row. But no, for the couple, thank you. Thank you for coming again.
But for those of you who weren't here last year with the easy to change code talk, I talked about this incremental change idea last year. But in getting some of your feedback, I realized that there were some problems with some of the things that I said. Like, I talked about notifications.
I said in one part of the talk, notifications are good because they promote loose coupling between different parts of your software system, and that's a good thing. But then in another part, I said, half jokingly, half seriously, that notifications are bad because they're just glorified go-to statements. So it kind of seems that both of these can't be right. But hang on, there's more. I also talked about in my session about hygiene, where the best writing is rewriting.
Quoting E.B. White, a great, great writer, But then I also said, don't throw away old code, right? You just kind of want to incrementally change software to sort of bring new features to it or change it. And this kind of seems, again, both of those can't be right.
That's like paradoxes, right? You wind up with this: Too many cooks spoil the broth, but many hands make light work. Right? Again, these things don't seem to make sense when you say them both at the same time. But of course they do, I think, in all of those cases, if you say one of those things in the right context. Right? Those statements don't exist in a vacuum. When they do, yeah, you can wind up with nonsense. But if you've got the context, right, if you know how to match one of those things, it can make sense.
This talk, Basics + Habits, is really the context, I think, for that incremental change that I talked about last year. It fills in more of the details. You can kind of understand, perhaps even go out on iTunes and look at last year's talk this year, sort of have your own little time travel. That's the context for your incremental change, Basics + Habits. Well, what does that mean? Basics. When I say basics, I mean it's the fundamental choices that you make at the start of a project.
You kind of make this whole series of choices, what your project is going to do, what your ideas, software you're going to use, all of those kinds of things. All of those fundamental choices you make at the beginning of a project. And then habits are the things that you do every day after that, building on the basics, and then you actually go and do all of the work, invest all of the time and effort to actually make the project come to life.
And so basics plus habits. Hopefully, you'll see that I've got some ideas today about how this will create this context that then you can add in your incremental change, some of those ideas from last year's talk, and eventually wind up with kind of a situation, a framework that will help you to make your software project successful and last a long time.
I've got six basics that I'd like to talk about today. We'll go through them. The first one being define your physics and chemistry. So physics and chemistry, I like this little analogy. You think about this in terms of software. Again, it's an analogy. Physics are the fundamental laws of your project, of your software world that you create in your software. And then chemistry are the way that things can mix together. I'll be using this analogy quite a bit. And here's the first example.
When I think of a very basic block diagram, a generic one, physics are the blue boxes. They're kind of the basic elements of your system. Particularly when you look at them from the outside, right? It's the basic physics, the real sort of unchangeable nuggets in your system, or at least fundamental nuggets. And then the arrows are the chemistry, or the way that then these things combine.
In this case, like a model view controller example, they combine in ways that, you know, in keeping with that MVC pattern. They combine in well-defined ways to make your software work in the way that it does. Right? So the physics and the chemistry. Now, I mean, this is such a basic idea, such a fundamental idea about how software works.
And I think there are other analogies that people use oftentimes, like nouns and verbs, where nouns are the abstractions and verbs are the way that things combine with each other. Another, you know, obviously very popular one is objects and interfaces, right? It's the encapsulated bits and then the exposed bits and the interfaces that you have to get at those exposed bits. And so forth. I still think that physics and chemistry, though, is the best analogy.
And again, I'll be returning to this quite a few times during the rest of the talk. And I do think that, you know, that ultimately, you know, most coding is chemistry, right? You're most often sort of using the interfaces that you create. You create interfaces much less often than you use them.
And of course, this chemistry is expressed in terms of those interfaces. It's expressed in terms of those physics. And so here now, if we go back and look at a more concrete example of that diagram that I had before, this basically, very basically, describes the design for the iOS keyboard, text entry system that I did a lot of work for.
This was my design for the 1.0 keyboard. As you can see at the top, I've got a keyboard controller. Over then on the input manager side, right? I've got the model. And then over on this side, I've got user interface, keyboard layout, text field, right? It really is sort of an MVC type pattern. Now, if I go in and add in the arrows, now, I'd like you to look closely. Perhaps even if you can't see how I'll describe the view side, the arrows are bidirectional. They go both ways. Yet on the model side, the arrows only go one way.
And this actually was an important design idea that I had very much at the beginning. Again, a basic level decision about how the software system was going to work. I didn't want the dictionary to be calling back up to the controller. No, no, no. The dictionary, the input manager system was called and returned to value.
That's all that it did. It only came into the system when it was called. Whereas, of course, the view system needed to respond to user touches and needed to, of course, be updated as appropriate. Maybe when an auto correction suggestion came in here and again. So the point here, though, is that this fundamental design was a basic decision I made at the beginning of the project and that I carried through to the rest of the system. wasn't like this. Very specifically, it wasn't like that where anything could call anything else. Right? And of course it wouldn't be a show if you didn't have flames, right? Okay. Now, this is a much more sort of sensible, basic design for an MVC system.
So the idea is, I think too, is that you want to strive for solid physics. Again, from that last example, those big blue boxes, they were sensible, right? Sensible design, sensible, big ideas for components of the system. And so you want to strive for that solid physics at the beginning of your project.
And you don't want to redefine the rules of your universe very lightly. Of course, we can't change gravity and the conservation of energy. And the physics we have in the natural world are decided for us. But in your software, you can change things just about as you want. Right? But don't. Right? With great power comes great responsibility.
Right? You want to kind of get your physics right at the start of your project if you can. Right? And perhaps maybe even the final way of talking about this, which is maybe a little bit more playful, maybe something that makes writing software so attractive to all of us is that you get to make a sandbox that you get to go and play with.
Right? Which is, again, it's sort of like you have that good feeling about a good, successful project, that you like what you've made and you enjoy going, improving it, making it better. And of course, all of you making great apps that then go out to customers, you get great feedback from them. And it feels good when you have a successful project like that.
So again, defining physics and chemistry, the first basic decision you need to make. Second, choosing the right technology. Make a technology choice for how you're going to make your project work right at the beginning. Now, of course, in choosing technology, right, you're here at WWC sort of preaching to the choir, right? You've chosen great technology. You've chosen Apple. You've chosen iOS and Mac OS, right? So congratulations. You're all awesome, right? Give yourself a round of applause.
Thank you so much. Of course, you've also chosen this talk this morning, so thank you for that. So, but now, sort of more to the point, right? If you think about tools when you're maybe going to build a house, you know that, you know, sometimes you need to go and grab a hammer and that's an appropriate tool, and sometimes you need to grab a wrench and that's an appropriate tool, but they're not appropriate for all tasks, pretty obvious. The same is true for software.
We've got some great software on our platforms. Core Data is a great example. It's a great piece of software for, you know, if you're developing an app, you've got some objects in your system, you know, at runtime, you want to maybe save some of the state out for those objects or save the objects themselves, use Core Data, save them to a database.
Boy, it's a great technology. It does that object relational mapping system. How many people, yes, my name is Ken Kocienda and I've made my own object relational mapping system, right? I have a problem. Right, I've done this many times. Core Data solves that problem. You don't have to do that yourself.
It's a great technology choice if you're an app developer, but if you're building a web search engine, maybe making a big sort of server farm, you know, whatever, probably Core Data is not the best choice, right, even though it's a great technology. It's not the great best choice for that particular task, right? So I think the message here, particularly at WWDC, is that if you're new to our platforms, you're going to have to be very careful.
Kind of new to iOS, new to Mac OS development. Learn our frameworks. Know what's available. Go to the labs. Find an Apple engineer. If you've got an idea, see if we've got software already there for you that you can take advantage of. Learn our frameworks. Know what's available, right? And figure out how to best match what you need to do to the task that you want to accomplish, right? Now, sometimes, even after you do that, you find out everything that's available, everything that we've got. And it doesn't quite match what you want to do. Sometimes then, but only from knowledge. You need to go outside the box. And again, there's, you know, an example that I'd like to tell you about is NSString in Foundation. Of course, it's great for general purpose programming.
But is it great for high performance string handling? High performance, I need to be making substrings, doing lots and lots of comparisons like, again, the example was the iPhone keyboard. When I was making the original auto correction algorithm, right, the first few tries that I made was basically brute force, searching the entire dictionary every time you typed a key.
Logically, that's what it was doing. But obviously, it couldn't do that. So I needed to have some high performance string routines to make that actually perform well. And so what I wound up doing was developing my own custom C++ string class. So I got a stack allocation using the short string optimization. You got to get a little sort of C++ geekness into every show. Not only that, yes, the short string optimization, which is now in libc++, the new C++ library that we're developing and making available as part of LLVM, which is really nice.
We didn't have that back in 2005. So should you do this? Probably not. Only, you only make a choice like that, go outside of the box when you've determined that you really need to. Because again, what we're providing to you as part of our frameworks don't quite fit the bill.
Now, I will say, interestingly, that even though that seems like a sort of a low-level optimization, was that premature optimization? And I don't really think that it is. And again, I think because this is an optimization that has to do with physics, I could have changed that string class out for another one with the same interface and it would have been all right. I didn't really paint myself into a corner.
In other words, this was an optimization that was a technology choice, not an activity, not making sort of very, very complicated algorithms that had a lot of interdependencies on each other, which then are much more difficult to undo if later you decide it doesn't really quite work or that you need to change your system.
Right? This kind of optimization, you know, in terms of physics, sometimes can be a little bit safer than optimization in chemistry. Right. Again, the real message here in this section is learn our frameworks. Come to our labs. After the show, go out on to our developer forums. Find an Apple engineer. Ask your question. Find, learn what we have available.
So that's basic number two. Basic number three is build solid abstractions. When you're thinking about your project up front, you kind of think about, again, kind of this may be even basic chemistry building blocks that you want to use for your code. And so I've got an example.
Here is a declaration of the dispatch async call part of the dispatch framework. Of course, this is a really, really great part of our frameworks. Of course, as it says, a function schedules a block for concurrent execution with the dispatch framework. Really, really neat technology. It's a great feature. It makes writing concurrent code much, much easier to do.
So, now that you know this, right, you've read the man page, you've read the developer documentation, and now you've got a piece of code like this where you've got a loop and you need to do expensive work with every object in an array and, ugh, you go in your profile and it's slow, right? So you figure, "Oh, I've got a couple of cores on this system.
I'll just go and fix it like that," right? I'll just go and dispatch async and do all this work on the background thread and it's just like, "Wahoo," right? No, not Wahoo. Why? Why is that not the right answer? Now, I think that what you've done, what that piece of code has done is that's now given you chemistry without physics, right? You just sprinkle in a little dispatch async.
"Probably not the right thing to do. And again, this is why it's not the right thing to do. You've got chemistry without physics. Concurrent code is complex. And I think that when you introduce that into your program, you need suitable abstractions to build on in order to make it come out right.
And so, I think the way to do that is in terms of objects and interfaces, right? Define your work in terms of the job that you want done. So now, right, so here is this little trivial example. Let's maybe use a different example that's a little bit more real world, which is maybe an image piece, image app that generates thumbnails for larger images. You'll see here, so I've got a thumbnail maker interface that says make a thumbnail, right? And then an observer which gives me a hooked call back after a thumbnail is finished being made.
So here's a piece of code to do this. I've got my photo program, which is itself a thumbnail maker observer. Maybe I've got now this interface that perhaps, you know, some other part of my software calls to say, okay, I've got an image now and I need a thumbnail for it.
I dispatch async onto a background cue to the thumbnail maker object, which is of course defined on the bottom of the screen to go and make a thumbnail. I pass myself in as the observer, right? So now, of course, that code goes and runs. It makes the thumbnail, churn, churn, churn, does the work, makes the thumbnail, dispatches async back to the observer, back to the main program, says it's done, okay. Thumbnail available now. Go and update the user interface. Take the little quick look.
I'm going to go to the gray box and replace it with the thumbnail, right? So very, very simple. But what I think you can see is that this is chemistry with physics, right? There's design, right? Dispatch async is really it's an implementation detail of this larger picture, which you could sketch on a board, right? You have your big boxes now, right? It's better.
That's a much, much better way to approach the problem. Again, kind of a trivial example, but I think it gets across a really important point. Because it now, you're thinking about the software now in terms of thumbnails, not in terms of just dispatching as part of a loop, right? You've got sort of a structure, a framework for your thinking. You've got an interface that will hopefully stand the test of time.
And additionally, if you need to change your implementation sometime in the future to maybe server-generated thumbnails, that you're not going to be shipping these images over a network, to have the thumbnail generated and have it come back, it's right, probably that interface, that might actually work. Instead of dispatch async, well, send the image, the raw, the full-resolution image over the network to have a thumbnail generated for it. So I think that's a much, much more solid abstraction. Again, defining your work in terms of the job that you want done.
And I think that's basic number three. Build solid abstractions. Okay, number four. Optimize for humans. I think this is something that is a good idea to always keep in mind as you're designing your project that particularly if it's going to have a long life that you're going to need to be maintaining it and you want to be kind to your future self today. So here is an example of that. A lot of times we use these data and wire formats in our program XML, JSON, PLIS, Google protocol buffers, what have you.
And here's an example of a little JSON structure that I might be getting. It's a coupon, right? It has an expiration date and name. Hey, and it's got a pitch too. Get 25% off on your next purchase. So now you might think that, well, if I'm going to be receiving this over the network from some service or partner, perhaps this is maybe I'm making a purchase.
I'm going to use this coupon, the next great coupon app, right? I might think that in my program then I'll receive this JSON over the network and then I'm going to need some Objective-C code to deal with that coupon. And I'll just write it like this. I'll just have a get property set property interface for the coupon and that's how I'll pick apart the details. And I think that's a bad idea.
Now you've got chemistry trumping your physics. Now you've got the fact that you're using JSON or one of these other wire formats making decisions about how your software is designed. You're not designing it for yourself. You're designing it for this wire format. And you wind up with a weird universe like that. Instead of something like this, you wind up with something like this.
You've overemphasized how data is traveling from one place to another rather than how you need to deal with it when it gets to your program. to your program. And I think this is bad because brain power is your scarcest resource. If you're trying to remember what's the property name? Was the pitch inside of a dictionary? Did I need to get this? You don't want to be worried about that.
It's not CPU power or network bandwidth that is going to be the limiting factor in making your software. It's brain power. And you want to conserve that as much as you can. So what can you do in a situation like this? Instead of having an interface, right, a weak object interface, get property, set property, I think you could do something like this instead, where you've got methods that take JSON data in, create your object, can also vend JSON data back out, and you can also do something like this. In case you need to send it back out on the network, perhaps even after modifying it, and then give yourself a nice, good, strong object interface to work with.
Now you don't need to remember, is the pitch inside of some kind of dictionary? If it is or isn't, it doesn't matter. You will have taken care of that, presumably, in the init method. And now that property is just available for you to go and use. Now you can go and see, maybe you've gone away from this code for a few months.
Now you can come back and look at the header and see what's in a coupon. You don't need to kind of go in with, do I have an example of one of those coupon JSON data is on a disk somewhere? You don't need to worry about that. Right? You've done the work to give yourself a good, strong object interface. You've optimized for yourself, right? Not for the serialization of the networking protocol. And again, I think that's something that you bake into your project at the beginning when you're making your fundamental interfaces, designing how your system works. So optimize for yourself.
Next, basic number five: Focus Your Development Effort. Focus. You know, I think at the beginning of the project, you think it's a good idea to ask yourself what it is that you're trying to be great at. You should have a good answer for that question. Right? What are you trying to be great at? Who's your audience? Who else in the world, once they find my app, is going to think that it's great? And why are they going to think that it's great? Right? You want to make sure you have good answers for those questions, right? And you want to make sure that you're building that then.
Right? That you're actually delivering on that greatness that you're trying to achieve. And I think that, of course, iOS and OS X give you great Sort of foundation to stand on, sort of literally and figuratively, right? Foundation framework, right? Stand on our shoulders. Build your great software using our frameworks. Learn them again.
A broken record for those of you who are old enough to know what that is. Learn our frameworks. What's kind of interesting too, it's just kind of a little digression, is that we wind up with sort of a recursive physics and chemistry. Now there are people obviously in Apple who for UIKit, that's their little chemistry set. They're going and developing UIKit, deciding what the physics and the chemistry is. For you, that's just physics.
UIKit is what it is. I mean, it's not changeable by you. That's just part of the physical reality, sort of the unchangeable law of your universe if you're working on iOS, right? And that's kind of interesting too. You might want to do that in your own software. You know, sort of develop libraries that then are sort of unchangeable by higher levels of code.
You might even want to do this for models, for the model of your program, and then have user interface level one for iPad, one for iPhone, that then treat that model layer as sort of an unchangeable physics. Kind of an interesting little idea, I think. Okay, so now more concrete examples, right? Which is sort of getting to the idea of that you want to focus on what makes your software novel and interesting. An example is, if you love the way that iOS device auto rotation works, don't spend time implementing that yourself.
You need to learn our frameworks and find out that UI View Controller provides that. Right? Similarly, if you need database interaction, you want to save some of your apps, runtime data in a database, learn about core data. Don't go invent your own object relational mapping software. If you need animations, learn about core animation, right? It's the same thing over and over. If you need X, and you're maybe not even quite sure what that is, what name to call it, because you don't know if we have that in our frameworks, ask in the labs.
That's why you're here this week. Go to the developer forums after this week, right? This week, since you're here, find an Apple engineer and ask your question. Get it answered, right? Because in the end, you want to be working on the thing that's going to make or break your application. Right? That's what you want to be working on. That's where you want to be spending your time. Focus your development effort right from the start. And then lastly, you always want to be looking to the horizon a little bit.
What if you're successful? What if your app works and people love it and are downloading it? What if maybe you've got maybe even a little service that's attached to it where people can upload content or download content or whatever? Can you handle it? Right? What about performance? How much headroom do you have in your software for things like performance or perhaps even for future features that you may not be able to get to in your 1.0 but you definitely know that you want to get to for your 2.0? So you really want to be building in a little bit of headroom if you can to your application. Think about how to do that.
Right? Another area to think about if you saw Henri's talk on Monday about internationalization, there's a huge opportunity out there for you to ship your app all around the world if you localize it into the language spoken by those people in other countries. We've got great support for internationalizing, localizing your app. String, dates, times, addresses, names, whatever. You want to be thinking about using our software right from the beginning so that when it comes time to localize, you're sort of in the ruts in the road.
You can kind of take advantage of our great localization features to ship your app all over the world. I really, really urge you to do that. It's a huge opportunity for you. And kind of getting back to that idea I said, you may have an idea for a 2.0 version of your program, or maybe you think of a great new feature after you've gotten feedback from users about your 1.0 release, right? Your next feature idea, will it blend? Well, it's kind of reasonable to believe that if you've wound up with a chemistry and physics like this, that adding a new feature maybe won't be so daunting.
But if at the end of, you've gotten to the end of your 1.0 and your design looks like this, it's because you kind of needed to hack in those features and you're just going as fast as you could and you really wanted to make a great release for your users by getting that one last feature in there and you needed to maybe make things a little bit more complicated than you would like. Look, that happens. That's real world. That absolutely happens all the time. But you don't want to be living with this, you know, then for your 2.0. And I'll kind of get to that, how you might fix that later on in the show.
Sort of burn that image in your mind of how that software might wind up looking and how you might want to fix it. We'll get back to it later. Maybe you need to do a stop and clean up and refact your code again. Talk about that a little bit later. But those are the basics, right? The basic ideas, at least a good number of them from my experience, the kinds of decisions that I make at the start of a project.
Things that I want to get right, right from the start. Right? It's the fundamental choices that you make. And then that takes us to habits. Once you've made those decisions, then you're going to be living with them and working and working and working in that physics and chemistry sandbox, to mix my metaphors, that you've made for yourself. And I've got six topics that I'd like to talk about in terms of habits.
[Transcript missing]
The second habit, thing that we do all the time, is we make changes to our projects. I think that changes, again this is sort of something I talked about last year with easy to change and incremental change, is that I think that obvious and unsurprising change is what you should be striving for. Smaller changes are better, you know, in sort of practical terms, I think is the takeaway there.
Right, so again, going back to the block diagram, so now let's say You've been working, working hard, it gets to Friday evening, maybe you check and do your last check-in, and that's how you leave the system. That's how it looked when you left to go home for the weekend.
So now let's say that somebody came in over the weekend, somebody else from your team, and added a feature, right? Changed that one object by maybe adding a new property to add a new feature to the software that you all knew about ahead of time, and the person just was enterprising, came in over the weekend and did it. Now you come back in on Monday morning and you see that change. Great. I knew about the feature. I knew that it was coming.
My teammate came in and did it. Feels good. You understand how everything works. Everything's fine. But now, what if instead you are working with a crazy developer? They're out there. It's like some of you. No, never mind. Crazy developer who got this whole new idea about how to re-architect everything, and you come back in on Monday morning and the system winds, it looks like this, right? Right. This never happens, right? This never, ever happens. No, it does happen, right? And now you're going Monday morning and it's just like, what happened to the project? That's not obvious or unsurprising at all.
It's shocking and horrifying, right? It probably means that you have bad communication. Probably this was the image that this engineer would have drawn on the whiteboard all the time and now secretly is going to impose it on you all, right? Not what you want, right? Not obvious or unsurprising change.
But you can make big changes to your software over time, right? Consider WebKit is a great example. I started working on WebKit in 2001 right from the beginning. And if you go and look at that WebKit and the WebKit that we have today, you'll see that there's been a huge amount of change. It's sort of like Theseus's ship.
There's probably not a single line of code that hasn't been changed, right? But is it the same software? I don't know if you know that story, right? You can make big changes to your software project a plank at a time, just like in Theseus's ship, right? A line of code at a time, a class at a time, a function at a time. If your changes are small, they're well documented like they are on WebKit, you sort of consider you have a good code review process where you're going and reviewing these changes.
The changes are communicated throughout the very large project in this case, right? And big changes are possible. The great thing about WebKit, if you want to learn about how changes are made, is that the changes are available since it's an open source project. Go and look at the, you know, at the web. You know, at the changes go by.
All right, so big changes are possible if, and they're possible by packaging them up as small, incremental changes. And again, it's all a matter of communication. You go step by step, make sure everybody is sort of bought in, knowledgeable about how the change is necessary, why the change is necessary, what it is, and so forth. So we make changes all the time, communicate.
And then number three, probably our favorite part, right? Is that you write code. Again, this is sort of tying back into the basic thing as your, the basic idea. You really do want to be writing code for each other. Ideally, your code, when and where possible, is self-documenting.
I couldn't resist. I had this line of code on the screen last year. This was an actual line of code that I pulled out of an earlier version of iOS. And even a year later, I still don't know what it does. It's that vexing exclamation point which changes the result of the ternary expression, right? I still don't know what this does and why it's written this way, right? Now, I'm not even going to let you look at it. You should probably be counting the braces and looking and saying, "You don't want to be doing that, right? You're not an IDE which matches braces. You don't want to be doing that. It's not worth your time, right?" So now, here's another example drawn from WebKit, actually.
You see it's the location text field for Safari. And I like to just call out a couple of lines of code, things that got added. This margin before URL got added at the top, a new constant. And then the later chart. And then the later chunk actually uses that. It's actually got a comment describing what this margin is for.
It's actually -- I don't have on the slide, but then the check-in comment described yet further other changes which may be coming to this code later on. And so the thing is you look at that, and it's not scary. If that developer made that change and then went on vacation, it's just like, "Oh, Ken, boy, yeah, the change came in.
Can you go in there and make that change that was mentioned in that check-in comment?" It's like I'd go and look at that code and say, "Yeah, yeah, I could do that and be pretty confident that I wouldn't mess it up." Because the code is written in an intelligible way, right? It's almost like it was written for me to come and understand it at some later point. So I think that's the takeaway for writing code. Ideally, you want it to be self-documenting. Write it for your teammates. Write it for your future self. Good habit to adopt. Okay. Number four. The review code.
Hopefully. Review our code. Give it to our teammates to look at, people working on the same project. Perhaps if you have an open source project, you have your community look at it before you check it in. So the example that I'd like to use here, this wonderful set of lectures that Richard Feynman did at Cornell in the '60s. Go out on the net and find them. They're absolutely wonderful. Seven lectures, each an hour long. Really, really wonderful. It's also wonderful to hear. A guy from New York talk about physics. What a real New York accent.
Really just wonderful to have. You just don't expect great truths about the natural world coming out from a guy that you'd see on the street corner in New York, or at least that's what he sounds like. Those wonderful shows. So now in one section, he talks about how physicists look for new physical laws. And he said it's a three-step process. He says first, you guess.
There might be a new physical law which does this. I don't know if it's right, but that's my guess. So you make a guess. And then you compute all of the implications of your guess. Well, what if this thing was true? What would that mean? Right? So you make all those computations and you wind up with your results, and then you compare those results to experimental observation, right? And if the results from your computation matches what the physical world seems to do, maybe the guess is right. And if it doesn't match, then it's wrong.
Very, very simple. That's how science works, right? What does this have to do with code reviews? I think it has a lot to do with it because it outlines a process that I think we go through in our mind, right? And that code reviews are stories and you want to tell the story of how you came up with the change really following that same procedure, right? Probably when you see a bug in your software and you're thinking now, it's like you're not searching for a physical law.
What you're searching for is why is this bug happening? You make a guess based on your knowledge of the system, right? Just like a way a physicist makes a guess based on knowledge of physical laws, right? So in the software, you make a guess. Why do you think, and maybe a guess as to how you're going to change your system, and you're sitting there guessing, well, why will the change that I haven't yet written but that I have in my mind and I'm about to type, why does that work? Why will that work? Why will that fix the bug? So you make your guess, and then you write your code, and then you compile it, and then you run it.
You actually have the software run, right? And then you need to go and look at the results and figure out how to test them, and then you compare those test results from how the program works, right? Did it actually fix the bug? You compare it to how your users will see it.
See if the bug is gone. You also want to ask yourself if you're sure that the bug is really gone, that maybe you actually understood it right from the beginning. And also, not only those three steps, but I think there's a very important fourth step that you want to communicate in your code reviews. I think you want to tell people about your guesses that didn't work.
If you made any. I make ten wrong guesses for every right one that I make. And I want to tell people about them because so often other people have a different perspective and they say, "You know, that first guess that you had was actually right if you had just done this." Or, "No, you read the bug wrong.
It's not about that. It's actually about this and your second guess was right." If you make this other little change. And of course if you'd never told any of the people about your failures in your code reviews, they wouldn't have had the opportunity to make that contribution or suggestion.
I think code reviews are stories and the best thing about this, this is one of the best ways to build that shared understanding that will help your whiteboard test come out right. Go and get somebody who works on some other part of your software, maybe not exactly in your area, and explain to them about this bug and your guesses and how you made this change.
That whole process will help them get more knowledge about how your particular subsystem works. Build that shared understanding. I think that's what code reviews are about. Forget about where the braces are and where the semicolons are and whether you've got the underscores. You need to do that, but that's not really what code reviews are about ultimately, about stories.
and building a shared understanding. Okay, number five. We test code, hopefully all the time. And again, so I have an example based on my experience of working in Safari, working on Safari all the time. Of course, you know, you go and make a code change, you know, in WebCore, you know, change the way the style system works and whatever.
And, you know, you compile that. It's just like, well, I've broken the entire Internet by going and making that change, right? Boy, it sure did happen. It's like I go and make the change, I just start up the web browser, and I load a page, and it's a white screen. Nothing works, right? You've broken everything. It absolutely happened all the time. So obviously you made a wrong guess. You got to go back and do something else.
Or the other joke was, Okay, so I made this change. You go and take websites beginning with A through M, and I'll take N through Z, and we'll get back together and see if our tests pass to see if that code change actually worked. But no, obviously you can't do that, so you need some other kind of testing regime. And in the Safari project back from the beginning, we came up with sort of this fundamental way of testing the browser, and it was built on, firstly, the page load test, the PLT.
I remember talking to Gramps one day about going and making the PLT and it turned out to be one of the best things that we ever did for the project. The idea was we had a performance test, it was built into the browser, easy to run, go to the develop menu, bring up the page load test, run the test, took the browser through a series of 40 well-chosen URLs, go and load them all. At the end, once it was finished running, it would give you one number saying this is how long it took. We did a little statistical computation, but basically the average time for loading all the pages. It was great.
Again, built in, easy to run, we ran it all the time. Typically how we would use this is we would get a bug, I need to fix this bug. First I'll run the PLT and see where everything is this morning. I'll go and make my code change, build the software, and now before I check it in I'll run the PLT again to see if I caused a performance problem. It wasn't a performance problem that I was fixing. 99% of the time it wasn't. Some other change.
Feature addition, correct, bug, correctness bug, what have you. By doing this all the time I wound up with a thick stack of yellow legal pad papers with just list and list and list, day after day after day after day, code change after code change of how the PLT was running.
It was just wonderful then later to go back and see that we were making the browser faster and faster. Simply because we had this easy to run test that we could rely on, run all the time to tell us how we were doing. This is great to do.
If you can build in a performance test like that and then really use it to drive your effort and never, I think the final key is never let any regressions check in. Or never check in any regressions. Never commit code that winds up making your program slower. Again, I think I said this last year, the great characteristic about that is that if you never make your software slower, it can only get faster or stay the same.
And so if you take a hard line on that, your performance won't help to improve. So many times you'd make a code change that seemed to have nothing to do with performance and you ran the PLT and it's just like, the code got faster. Wait a minute. It's a wonderful bonus and we'd go and investigate and a lot of times that actually pointed us to another direction where we could get even more performance gains. Just some aspect of the system changed which actually turned out to have a good effect. It's actually a good mutation, right? It does happen.
All right, so the page load test, build in, those testing, and that performance. The second kind of test that we added, layout tests. These are correctness tests. These are basically just web pages where you load them. We would print out the render tree structure for the web browser, take a screenshot of that page, and then we would have a means to compare before and after to see whether or not a change altered the correct rendering. For web content. This is also a great thing to add to your code.
Um, you know, having this system that's easy to run, we could run this from the command line, adding a test was as simple as just going and putting a new HTML file in a directory in the project. When you added that, the system would see that it didn't have expected results and would just generate them right then and there. It was just so simple and wonderful and easy to add tests.
If you're in the WebKit project, it's like you know that you can't check in anything that has to do with rendering without adding a layout test. So we've got thousands of tests now. And the other two great things about both of those kinds of tests, you think about them as unit tests, but they're really not.
They're really whole program tests. When you're testing rendering, obviously it's also testing, you know, network loading, and it's testing whole other aspects of the system. You know, HTML parsing, you know, style parsing, you know, all kinds of parts of the system that I may not be working on right now, but I'm still exercising and making sure that I haven't caused any... "Unintended regressions.
Of course, really the end result of all of this is that it gives you confidence that your changes are not having these hidden evil effects on your software. You go and you make a change, you run all your tests, and it gives you confidence that you can check in that change without having any ill effects that you didn't intend.
You get confidence from your tests, which is great." That's number five. Number six, the last habit, is that you reevaluate your basics. And what this does is it sort of kicks off the feedback loop back up to the top of your basic decisions. Right, you made your basic decisions, you did the best that you could with them, you lived with them for a while, you have all your habits, you write your software, whatever, and then at some point, a lot of times, you realize, man, boy, that decision to design that object that way didn't really work.
We can't change it now, but maybe later on, before we do 2.0, we're going to go and make a change with that. A great example of this was I did the work to add HTML editing to WebKit, which of course is now used in Mail and OS X. It's used for all of the text editing that we have on iOS.
So while I was doing that, the initial idea for where the insertion point was blinking, which I tell you is the hardest bit of code that I ever wrote in my life, was to take an arbitrary bit of web content and be able to just arrow through it one character at a time to the end of the content and then back.
It's just absolutely, really, just incredibly difficult to do for web content. And the problem was I had insufficiently good physical notions about how to do that. I had a position in a DOM document which turned out to be not powerful enough because there were positions which were visible and positions which were not visible.
So I found two of my very, very smart friends. I found Darren Adler and Trey Madison, if you know those two people. It's just the guy who, you know, Darren Adler you may know. And so, of course, he leads the WebKit project now and he also was leader of the Blue Minis on System 7.
And Trey Madison, all he did was invent apps. He invented the WebKit. So I found those two guys and I said, "Guys, you got to help me here." Right? And all I was smart enough to do was to find the smart people and they introduced this notion of a visible position, a new physical construct. It was only a position that when you arrowed, you had an iterator on the visible position that would just go to the next visible position.
And all of that smarts which used to be sprinkled out through the system got gathered up into one physical notion. And it made sense. It made the whole sort of chemistry change, the whole physics and chemistry balance of the system and made it actually possible to get that code written correctly.
Again, that was a whole story having to do with reevaluating a basic decision after living with it for a while at the habits point. So now, what do you really wind up with? What does this wind up looking like? You've got your basics, you go and you do your basics, you kind of get down to the bottom here and you just go back to the beginning at some later point.
If you remember that image that I told you to burn into your memory, you wound up having this sort of exploded spaghetti factory, sort of chemistry. You know, you probably want to reevaluate that at some point and this is the time to do it. When you're going back up to the top to your basics, reevaluate your basics as a habit.
Right? And I think that what that gives is a process for long-term change. Kind of go through your basics, live with it, and then go back to the top and start again. Maybe at the boundary of major releases, you know, maybe when you realize that you've got this fundamental performance problem which is just seems intractable, maybe go back and reevaluate some of your basic notions about how your software works and maybe that's the way to get through and solve the problem.