This is Water
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Ford, Neal
This talk describes the water you swim in but cannot see anymore, like relational databases and application servers. Additionally, it jerks you out of safe, warm water (briefly) to describe a strange, fantastical world with things like immutable database server, phoenix machines, and lambdas.
 Good morning everyone. This is water. Two young fish were swimming along one day and an older fish swam up and said, good morning boys, how's the water? And the older fish swam away and one of the young fish looked at the other one and said, what the hell is water? So this is a little story that's told by one of my favorite authors in the 20th century, David Foster Wallace in a keynote address that he did for a university commencement address for graduation speech at a university. And his point in this story was telling these grad students that the really important things in life are not necessarily the things you learn in university. It's the way that you interact with other people. It's the way that you live your life. And he's trying to get them to notice the things that are so common. They're just like water around a fish and you never even think about them anymore. But sometimes there's real value in looking at those things with fresh eyes. And that's exactly what I'm going to do in this talk from a technology standpoint is point out all the places in your world where things are so common that you just think of them as water. But I'm going to encourage you to take a step back and look at where you're swimming to see if maybe you could be swimming in a better place. And this is kind of exemplified by three Greek letters, which is going to be my agenda for this talk. Lambda, mu and delta. So let's start with mu. Mu, when it's done, used in mathematics, is typically the force of friction. It's a measurement of friction. So when you talk about mu and physics, this is usually the standard constant they use when they talk about friction between physical bodies between things. But when we think about friction from a software standpoint, we think about artificial friction that has been introduced accidentally on projects by doing things inefficiently because you're using a tool in a less effective way or you're doing something that seems like a good idea in the micro, but maybe in the macro is not such a great idea. So let's talk about some common kind of frictions that pop up because one of the things we're trying to do in the engineering practices of continuous delivery is take a holistic view of all the engineering practices on a project and make sure they're all working nicely with each other. But let me give you an example of where projects often fail with this. And it's around this idea of continuous integration. We think about continuous integration now as a best practice and very, and kind of agile kind of projects. But there are really two aspects of continuous integration. There's the practice of continuous integration, which people do as they integrate things, but there's also the machinery of continuous integration. And most of you, when you think about continuous integration, you think about the machinery, continuous integration servers that do all this work for you. And you've forgotten kind of about the practice, but I want to bring this back to the practice of continuous integration, which is exemplified by the extreme programming guys, basically said, translated in modern terminology, everyone commits to the trunk at least once a day. And this is still really good advice, but there are projects that accidentally throw this away. And so what this is really an example of is something that the XP guys, extreme programming guys, identified around continuous integration and a bunch of other things in the software world. That when you think about the amount of pain or effort that you have to expend over time, you would think that as your problem gets gradually more sophisticated and complicated, then the amount of pain that you pay over time for engineering practice would go up linearly, which seems natural. But it turns out that many things in software, the pain doesn't go up linearly, it goes up exponentially. And so you hear people talk about doing things, bringing the pain forward. This is one of the mantras in the extreme programming guys, the observation being for things like continuous integration, if you do it more often, you end up paying less total cost, and if you put it off for a long time and then deal with the combinatorial explosion of all those combined elements. In the continuous delivery world, we talk about doing things as eagerly as possible versus doing them as late as possible. And so let me give you an example of where people accidentally throw away continuous integration. So let's say you have a situation where you're doing mainline development on a trunk in version control, but there are a couple of features that may or may not make it into the next release of your software. One of the ways you could model this problem from an engineering practice standpoint is to use something called feature branches. This is the engineering practice. And the idea is to use branches in version control to manage the speculative in-flight features that may or may not make it into the next version of our software. And so the mainline is where regular development is still going on. Reverend Green takes one of these in-flight speculative features and Professor Plum takes the other one, and they start doing work on their branches. Now, both Professor Plum and Reverend Green being well-established version control citizens know that as you make changes, you should periodically pull from the trunk because things are still happening on the trunk, and so as you make changes on your branch, you pull from the trunk and handle those little merge conflicts that come up. And maybe a few days go by, maybe a few weeks, a couple of months go by, and it's decided we love Professor Plum's feature. We want it in the next release of our software, and so Professor Plum pushes to trunk and performs what we refer to as a merge ambush on Reverend Green. Now, Reverend Green didn't do anything wrong. He came into work on Monday morning, did SVN up, and now slam. He's got a month's worth of merge conflicts to deal with, which really sucks for Reverend Green. But there's a deeper problem lurking here as well. What happens if on the very first day of their branch, Reverend Green started making profound changes to the customer class? And the first day of his branch, Professor Plum started making profound changes to the customer class in the opposite direction. They've now essentially been working in isolation for a couple of months from one another, and now Reverend Green has a terrible decision to make. Though I reconcile my vision of customer with Professor Plum and potentially undo a couple of months' worth of work, or what I'm terrified of, is I'll just create a second version of the customer class, and we'll let those two things diverge into the future forever. The failing here is that we've traded one engineering practice for another. We've traded feature branching for continuous integration, and it turns out it was a really bad deal because we gave up a lot in giving up continuous integration. And so one of the things that we talk about in the continuous delivery world is you could solve some of that pain by actually doing proper continuous integration on branches, but we don't want to use branches at all for this. There's a whole bunch of practices in this world called trunk-based development that allow you to stay on trunk and keep doing continuous integration and still isolate features that are in flight or isolate things that are not quite done yet. There are ways to do this. Engineering practices called feature toggles or feature flags, or the strangler pattern is another way to do this. This allows you to stay on the trunk and continue doing continuous integration and not accidentally throw it out of the way by picking up some other engineering practice like feature branches. And this actually highlights a big problem in our world, is that very often meta-work is more interesting than work. So how many of you would say if you really boiled it down to its essence? If you had to describe your job to your grandmother that it would ultimately boil down to, well, I take things from web pages and put them in relational databases and I think things from relational databases and put them on web pages. So how many would say that that kind of if you boil it down to its essence, that's kind of what you do as you're living? The problem is that's not very exciting. If you've done that for 20 or 30 times already, that's not very exciting. You know what developers do when their day job is boring? They invent cool puzzles to solve so that they'll have cool puzzles to solve when they come into work. And this is where branching strategies end up in most of the modernization I've seen that heavily use branches. They have built a Byzantine structure of branches that no human can understand anymore because that's a really good meaty problem that you can sink your teeth into every single day when you come into work. It's not making your software any better, but it's certainly making your job a lot more interesting. And maybe that's not the best thing. This is exemplified back from the 90s. Jamie Zwiske wrote this quote that he referred to as the CADT model of development. CADT stands for the Cascade of Attention Deficit Teenagers Model of Software Development. And he makes this observation that he was talking about GNOME, the Linux shell. And he said the problem is every time a new version of GNOME comes out, he posts bugs and those bugs languish for like two years until they get canceled because a new version has come out that has a whole new set of bugs. And he points out fixing bugs isn't fun. Going through a bug list isn't fun, but writing everything from scratch is fun because this time it will be done right. So this is what happens over and over again. This is the water that many of you live in on a daily basis. So that is friction. Let's talk about change and delta. And math, delta, means the change of something. But let's talk about change from a technology standpoint. And talk about something that is probably a very common experience for many of you because when you were very young, you got interested in computers, so you started playing with computers a lot, maybe computer games, you started hanging out with a bunch of other people who were really in computers. And probably that was increasingly more and more male, male, male dominated. You went to university and majored in computer science and computer information systems or something like that. And it was predominantly male. And so when you come out into the world, mostly what you've been used to dealing with are males, very little diversity. So if you look at this very famous picture from the first picture from Microsoft, this is Microsoft in 1972, and you'll notice in this picture that they have three women. Sorry, two women in this picture. One of them is a very, very young Bill Gates. So they have two women here, which means the ratio on Microsoft in its early days was 11 to two. And unfortunately, that ratio has not changed much. Here are some studies from Forbes from a couple of years ago, 10 to 12% are female, 17% for women, 7% are developers were women. This is unfortunately the water that we're swimming in right now is a severe lack of diversity on software projects. And nobody's really talking about this very much, which is bad. Here's a developer survey from Stack Overflow that just recently did a developer survey. And this shows even worse that 6% of the women in the Stack Overflow, 6% of the respondents were women in the Stack Overflow survey. This is bad for obvious reasons because having monocultures is bad, but it's also bad from an intellectual standpoint. Because of this Harvard study that shows that if you have women on a team of people who are doing intellectual work, then the presence of women raises the collective IQ of the entire group. It's not that you've added a smart woman and made the average higher, the presence of a woman raises the collective IQ of the entire group of people. And that effect works up until you get 50% of women on the project. It has to do with socialization and how information is conveyed and just a general kind of betterness that comes from having diversity on projects. And notice this is the collective intelligence of the project. And so if I went to your boss and said, I could immediately make everybody on your team 10 to 20% more productive with a very simple change that obviously say, well, yeah, that's clear, hire more women is the way to do that. We need better ways to foster diversity for obvious reasons, but for not obvious reasons too, which is the fact that it'll make your projects a lot better with a lot less effort because you have more intellectual diversity. So let's talk about another form of change, some water that you're so used to you don't even think about it anymore. And that's transactions. This becomes a big deal for a lot of you as you transition from monolithic style architectures to more service or micro service based architectures. Those transactions are easy when you're in the monolithic world, you got a monolithic application, you got a monolithic database, it's easy to wire up transactions to happen there. And so that becomes much more difficult when you move to a much more distributed kind of architecture. But this is actually a huge disservice that we've done to users that trying to get them convinced and transactions are a realistic thing in the world where they're not. This is purely a kind of artifact of computer science. One of my colleagues several years ago famously made the observation that your local coffee shop does not do a two phase commit. When you go to your local coffee shop you pay them for coffee and you get a little slip of paper but you walk to another part of the store and you eventually get a cup of coffee. That's a ventral consistency in a coffee shop. What would a proper two phase commit in a coffee shop look like? We'd hand them money and they'd hand you a cup of coffee and you'd let them go at the same time. So that's a proper two phase commit in a coffee shop. So why don't coffee shops do that? Would it not make you feel better as a coffee member buying in the public if you're coffee shop that a proper two phase commit? The reason they don't do that of course is because it's screamingly inefficient to do two phase commits in the real world. Everybody's example for how transactions work are banks. But no bank in the world, in particular no ATM in the world does what we would call a two phase commit for two reasons. One of which they're always designed to work in offline mode in case their connection to the bank gets severed. They can still operate. But the more pragmatic reason is nobody wants to stand around in an ATM for five or ten minutes waiting for a query onto some batch job onto a mainframe to tell them how much money you have in your account. Because you notice that banks still typically do reconciliation in the middle of the night. You know what reconciliation is? It's eventual consistency. Banks don't do transactions. In fact, banks existed before computers existed and used to bank records traveled on horseback and there is no two phase commit when there's a pony involved. So we've got to get our users away from this idea that transactions are a natural part of the universe because they're really not a natural part of the universe. They're an artifact of things we can do with particular kinds of computer architecture. But we're gradually moving away from the architectures that make that easy and so we're going to have to reconcile this. And so you hear a lot of emphasis on base and eventual consistency versus transactions as you get more and more sophisticated in the services world. So this is the asset versus base discussion. You're all familiar of course with the characteristics of acid, atomic consistent, isolated and durable. Base and a bunch of variations of base are things like basic availability, soft state and eventual consistency. The reason that everyone is eventually moving to eventual consistency is that as you scale up the number of users that you have and you're forced to scale the distribution of components more and more and more, you always eventually prefer base to acid because of the cap theorem. This is a mathematical theorem that says that once you reach a certain level of physical distribution of nodes, then you can no longer guarantee all four characteristics of acid anymore. And so you see really giant, huge code bases move more to eventual consistency because of cap theorem and just because the complexities that have in transactions running around causes on projects. Another kind of welcome change that has happened over the last few years is the end of the relational database dark ages. We now have lots of options in terms of persistence. Ten years ago, it was automatically assumed that every project you worked on would be backed by some sort of industrial strength relational database. That was just not even a question. And so now this is more of a question. So now we're starting to notice the water that we're swimming in and choosing the right side kind of persistence for the kind of problem we're facing. Another kind of water that you're swimming in all the time and may not realize it has to do with the way your teams are structured. So the very first O'Reilly Software Architecture Conference happened in Boston this last March. And within the conference, Conway's Law became sort of an inside joke because every single talk mentioned it in some form or another. So it became almost a drinking game within the conference with how quickly is this talk going to mention Conway's Law. This is an observation that Melvin Conway made back in 1968. It says that any organization that design systems will produce a design whose structure is a copy of that organization's communication structure. Besides being a little needlessly wordy, what Melvin Conway was talking about here was that from a software standpoint, if you have a bunch of UI specialists and middleware specialists and DBAs running around in your organization, you will probably end up with an architecture that reflects that communication structure that exists within the people of the organization. This is what from a continuous delivery standpoint we refer to as a human resources optimized organization. So if you're trying to build the best human resources organization in the world, this is the way to do that. But this is the water that you're drowning in right now because many of you are trying to build an architecture that doesn't look like this and you're finding it very difficult. That's because of Conway's Law. And so one of the things that we talk about is the inverse Conway maneuver. Build teams that look like the architecture you want and then architecture will be easy to follow. Actually, there's another name for this that an enterprise architect came up with and I'll give you this one in case you want to make it sound more official to your management. So inverse Conway maneuver is one version of this. Domain architecture isomorphism is the other. It means the same thing, but the second one sounds a lot more important than the inverse Conway maneuver. If you're really trying to impress a boss or something like that, domain architecture isomorphism is the way you want to go in the future, which is basically just saying build the teams that look like the architecture you want and it's easier to achieve that. So what that means from a practical standpoint is instead of organizing people like this, organize them in these cross functional teams and very commonly now in the services-based world, you organize these things around the domain entities that you've identified in your code base. And these cross functional teams will have developers and business analysts and DBAs and operational people, pretty much everything required to make this thing work in an operational kind of context. You see a lot of interest in shifting team structure to accommodate architecture now, which is a pretty good idea, I think. Here's another bit of water that you're drowning in from an architecture standpoint. Because one of the goals of a lot of architects is to create single sources of truth for things. It's just a natural instinct to say, well, we have a lot of information about customers running around this organization. Let's consolidate it all into one place. We have a single thing that knows all about customer stuff. That's a natural instinct because it's a logical thing to do. And so we had a client who did this. They run an airline and their architects have put together a really nice state of the art kind of customer service and was backed by clustered application servers and databases and all that good stuff. And then a volcano exploded in Iceland. And pretty much everyone who had a ticket on this airline called them up and said, hey, is that volcano going to affect my flight? And it turns out that people on the other side of the world who had a ticket on this airline couldn't board their planes because the customer service essentially fell over. Because they built a single source of truth for everything. That means that now customer service calls need to access the customer service boarding needs to access the customer service because they need to look up passenger information through that service. And so by following their instincts as an architect to build a single source of truth for stuff, what they did was build an operational bottleneck from a business standpoint. They would have been much better off if customer service and have one piece of customer and boarding and had another piece of customer and other parts that had another piece of customer. And there might have even been duplication between some of that information in some places, but from an organizational standpoint, that would have been better than having one beautiful source of truth that fell over in realistic kind of, albeit unanticipated, but realistic kind of scenarios. And so this is a perspective shift that I think architects need to take on that I've been started talking about recently. That when you look at the jobs that many architects do, they draw lines and boxes and connect things together. This is a really good snapshot of the topology of architecture. This is a two dimensional view of architecture, which is a valuable thing to have. You need to understand the information flow and how the boxes are all fit together, etc. But many architects stop here. And that's too bad because this is not nearly detailed enough to be practical. The next thing you need to do is flesh this out and say, well, the boxcode RM has to be a specific version of a piece of ORM software. And I have to be able to get that to work in operations with all the other stuff that's working in operations. So you need to flesh this out and make it three dimensional. This is a 3D view of software. But this is not far enough either because now if you really want to be responsible for an architecture standpoint, you need to think about, well, a new version of hibernate is going to come out in six months. And I need to be able to update to that without wrecking my whole architecture in the process. This is a four dimensional view of architecture. This is the modern perspective on architecture that encompasses not just boxes and lines, but also the operational constraints that you have to handle as part of your architecture. And how do you evolve that architecture over time? This is a lot of my personal emphasis right now is working on ways to build better evolutionary architectures that really take into account both the lines and boxes, but also all the operational aspects that it takes to get architecture up and running and evolve it over time. And so when you look at the problem we've been trying to solve with architecture over the last 15 or 20 years or so, we've been getting better and better at building shared resource style architectures. This is what model is really good for. Application servers can create multiple thread pools and create database connection pools and make really effective use of shared resources. But remember yesterday's best practice is tomorrow's anti pattern because a funny thing happened over the last few years. Resources became dirt cheap. In fact, you can rent resources from Amazon for pennies now. And now our biggest architectural problem is all the coupling that we introduce so that we could share resources and those previous architectural styles. And the problem here is that it makes isolated change hard. If I need to change the ORM version for the customer service piece of my monolith, but I don't want to change it for the other parts of my monolith, I can't do that because they're all coupled together. It encourages big bank style deployments. It makes upgrades difficult. Ultimately what this does is make change expensive and make and prevents change. And which is what we're trying to get away from. And again, this idea that yesterday's best practice is tomorrow's anti pattern. What we're trying to build in modern architectures like microservices are ones that support change as a first class citizen. So when you look at a traditional service oriented architecture, this is very much a maximized shared resources style of architecture, which is still perfectly good if that's your main goal is to maximize shared resources. But when you look at a microservice style architecture, the main goal here is to emphasize easy evolution so that you really do have these kind of Lego components where operationally these things are not coupled to anything else in your ecosystem. They know how to integrate with all the other pieces, but you can easily remove one of these and replace it with something else that adheres to the same contract and not have to make any kind of ripple changes to the rest of the ecosystem. And so that's really the goal here in microservices in particular is to build architectures that support rather than punish change. And that's all about change. The last thing I'll talk about here is lambda, which as you know relates to functional programming. And this requires a little bit of a story about me. When I was back in university, we used a piece of software called pecan Pascal anybody here ever heard of pecan Pascal be careful you date yourself if you say yes to this question. So the cool thing about pecan Pascal was you could write the same source code in Pascal and it would run on both an IBM PC and an Apple too. Because they were this is in about 1983, 82 or 83. And they were using this technology called bytecode. So when you compiled your Pascal program, you compiled it to this bytecode stuff and then it ran on a virtual machine on the actual computer. And it turns out it was slower than Christmas because hardware at the time just couldn't really support that level of indirection. And so this was impractical even for university projects. But then you fast forward about a decade and you have Java, which uses the exact same kind of architecture. But now resources are enough to accommodate the sophisticated things that we got with Java. And this is a trend that we see in software that over time we see more and more busy work to our languages and runtime. So we gave up memory allocation garbage collection with Java, which is almost all universally a good thing. You can give up now states with closures and functional programming languages, things like caching, which I'll talk about in a second. You never have to hand write a cache with a hash table ever again if you don't want to even sophisticated things like concurrency. Closure does for concurrency what Java did for garbage collection, meaning that in closure you can write really high performance concurrent applications and never once think about concurrency because the language is handling all those details for you. And this is a good thing in my opinion because I did a bunch of coding in languages like C++ and Pascal and I reached a really important conclusion. Life is too short for malloc. The way too low level for me to be concerned with, I would much rather spend my time thinking about the problems I'm trying to solve versus the plumbing of how all those things end up getting solved. And that's really what functional programming is about is getting you at a higher level of abstraction away from plumbing. So let me give you some examples of this. I'm going to start with a quote by Michael Feathers, who's well known as the author of Working with Legacy Code. He made this observation on Twitter and it's a very acute observation. This is why it's a little bit terse. And Mr. Feathers makes the observation that object orientation makes code understandable by encapsulating moving parts. So you think about the things that you know about languages like Java and C sharp. There are lots of mechanisms to hide information, make it available to some people, not others. Some people can't see it at all, but some people have full access to it. Those are all moving parts around state. Functional programming makes code understandable by minimizing moving parts. The theory being if you don't have to interact with it, you can't mess it up. This is like garbage collection Java. You never worry about memory leaks in Java applications because the runtime just handles that for you. So that part is just moved away from you so you never have to worry about it. So let's look at some examples of this. One of the common things that you have in your world are things like design patterns, which is a really useful catalog of things that you can do. But a lot of these have been deprecated if you have a functional programming language. So for example, let's say that you have this code. This is the target of a little refactoring exercise. What it does is not that important. What's important is the observation here that you know what, if you look at this closely, the only two specific bits of code here are these two right here. And so if I made those generic, I could make this, harvest this as the command design pattern. And so that's exactly what you do for this. The command design pattern is harvest a command instance and then execute it within the block of this rapid transaction and then create a little anonymous in our class and do all that business to handle that level of encapsulation. But it turns out the command design pattern is completely not needed at all if you have first class functions in your language. The command design pattern is really just a bandaid over the deficiency that your language doesn't have a way to pass behavior very easily. So if you look at the same code in Groovy, which does have closures, of course Java has closures now, you can actually do this with way less formality because this is a feature built into the language. So first class functions deprecate the command design pattern. You never have to use the command design pattern again once you move to a language that actually has lambdas like Java 8. This is a great example of eliminating moving parts from you because you don't have to worry about all that scaffolding anymore. The language just handles that level of encapsulation for you. But let me show you an even broader example of that with memoization. So here's some code and what it does is not that particularly important. This is actually some Groovy code that classifies a number as either perfect, abundant, or deficient. And what I really care about here is efficiency. So in particular, look at this factors of function there. I want to make that more efficient and actually some of factors is the one I'm going to attack here. Because what this code does is classify numbers and I may pass a single number in and it'll do is perfect, but it may, if that's not perfect, then it may come back and say, well, is it abundant or is it deficient? So you may test it more than one time. And so what I want to do is that some of factors where I'm having to add everything up, I want to make it more efficient by creating cash for it and like this. So again, this is Groovy code. So I create a little sum cash and then if when I do some of factors, if it's not in the cash, then I'll calculate it, put it in the cash and then return it. Otherwise, you should return that value. So this is using a hash table to create a little cash so that you don't have to do extra calculations that you need to. Turns out you never have to write one of these anymore because a lot of functional programming languages have this feature called memoization built in. So as long as some factors there is a pure function, meaning has no side effects outside of parameters passed into it, which is true in this case, I can create an alternate version of some factors called sum of factors where I say some factors dot memoize. Now some of factors is a function that it will automatically cash values for me just like my handwritten one does. But this is managed at the runtime level rather than some code that I had to write. This is a classic example of eliminating moving parts. You never have to hand write a cash ever again because it's now built into the language or runtime. Now I wrote this example as part of my book functional thinking, which is a book about shifting to functional thinking in the mostly Java ecosystem. And I tried really, really hard to create a handwritten cash that would perform better than the memoization stuff that's built into groovy. And I failed utterly. You know why? Because they cheat. They have access to internal data structures and other things that I don't have access to. And that's probably a good thing because I don't understand them deeply enough to really optimize it and make it work well. But the language designers do. It's just like garbage collection Java. Why are you handwriting something that the language is just handing you in many situations. So memoizations built into groovy and closure. It's trivial to implement languages like Scala and Java 8. So you never have to hand write something like a cash ever again. But here's the real benefit of shifting to functional programming. This is the place where this is where it really is like water because you've seen Java 8 come out. It's like, oh yeah, you can do lambdas in Java 8. Okay, that's cool. Well, we don't really need that. So we'll just keep going with what we're doing now. Here's the real benefit. So let's say that you have this as your company process. You're given a list of names and you're asked to remove the single letter entries, capitalize the first letter of each one and then return this as a comma separated list. So this is your job as a developer and you write this in pre lambda Java code and it looks like this. Clean names builds up a string builder and then for each one of the list of names. If the length is greater than one and capitalize it and append it to the list and then get rid of the last trailing comma that's left over when things are all said and done. But let's improve this by shifting it to Java 8. So here's the Java 8 company process. Now I can say names dot stream streaming library in Java 8 allows you to attach behaviors to that stream. One of those things filters out all the entries where the length is not as greater than one gives me the ones that were linked as great as one and then capitalizes all of them and collects all of them together joining them with a comma. The collectors dot joining with a comma is smart enough to know that you don't want to comment the very end of the list when you're done and so it automatically doesn't put the last one on the list which I had to get rid of by hand in the previous version. So this now looks very close to the algorithm that describes what I was supposed to do. Which is nice. This is a way more readable now much more understandable by what's going on. Let's say then that your boss comes along and says hey you know that name cleaning thing you wrote. Awesome. It's doing a really good job. But we need to work at scale now and you need to make it multi threaded. Oh great. Now we got to go back and pull that for loop apart. Stop worrying about synchronization blocks and all that stuff in the Java 8 version. You just convert it to a parallel stream and you're all done. This makes concurrency more or less invisible. Now this is not completely invisible like garbage collection. You still have to understand some things about concurrency. But you don't have to understand and code every single detail of the plumbing anymore. Most of this is handled for you. So there's this great thing that the closure community introduced. So closure is of course a really nice functional language. I'll talk more about it in just a second. But the creator of closure Rich Hickey is also a big word fan and he has resurrected an archaic term which is a really nice term that describes a lot of software. This term complete things are completed means that you intertwine or braid them together inadvertently. And this is exactly the problem with the pre lambda Java code. If you look at that code in the upper left hand corner you could make that code a lot clearer if you did three distinct for loops one for loop to get rid of the single letter entries one for loop to capitalize everything and then another one to catenate them all together with commas. But you don't do that do you. The reason you don't do that is because it'd be really really inefficient to loop through those things all at once. And so you just complex the problem together into the for loop because that's your main mechanism and you want efficiency. What. Lambda is in Java eight allow you to do is untangle those things and make a much clearer representation of the problem you're trying to solve. This is the real value of switching to something like Java eight and functional programming because it allows you to operate at a higher level of abstraction further away from the plumbing closer to the problem you're trying to solve in a language that's closer to the problem that you're trying to solve. But let's talk at a lower level here because when you look at object oriented languages and the way they want to reuse stuff it's always very big. So when you look at for example the gang of four book every single example of the gang of four book is a network of classes that interact with one another. So the common best practice in the object or programming world when you solve a problem in a particularly elegant way you take the piece of that that's really elegant and you package it up for later reuse in a jar somewhere so that you can pass this around and do stuff with it later on. So in functional programming languages try to achieve reuse at a lower level because in functional programming languages they really want to use pre existing algorithms and mechanisms that have already been highly optimized so for example in the Java world how many XML libraries are there. There's got to be at least 10 or 12 in common use and probably 50 or 60 of them in total and every single one of them has unique data structures inside them that are completely encapsulated and hidden away which is all fine and good but a very very coarse level of reuse the way that functional programming languages handle things like XML is they immediately take XML and mash it into a map data structure that they already know how to operate on and they have really optimized mechanism to work on things like maps. And so code reuse happens in a much smaller more granular level in functional programming languages where you have really highly optimized mechanisms that pre exist for operating on common kinds of things like lists and sets maps etc. You have data plus code blocks as closures that customize the way the machinery works. You pass that stuff into the machinery and it passes you back something out that's transformed in some way or another. So I want to show you a concrete example of this. I'm going to take something that's probably in your code base for using Java the Apache Commons string utility index of any and I'm going to gradually morph this over into closure code and then talk about the differences. So here's what index of any does this is part of string utility and so you pass it a string and an array of characters and it will tell you the first care occurrence of one of those characters in the string. So if you look at the third one up here for example there's no there's no will see shows up the zero position this string B shows up in the zero and two fourth position third position year zero one two three that's the index where it shows up doesn't show up at all there so that's what index of any does. Here's the code from Apache commas that does that in Java so I'm going to gradually one piece of the time morph this over into closure code and look at what we have left. So the first thing I'm going to do here simplify the corner cases in the closure world it reacts properly to empty strings and things that are empty or null and it doesn't do anything to them so there's essentially no ops. And so for the closure version I can basically get rid of those corner cases and so when I have search characters then I need to do this stuff to handle that case. Now I can get rid of types closure is a strongly but dynamically typed language so you never predefined types enclosures so I can get rid of the type declarations and simplify it down down to there. Now I need to go in and get rid of this inner for loop right here basically what that's doing is say for each one of these search characters you find one of them return the index where you found that search character. So I'm going to simplify that say when you find the search character of CH then return the index for that. And now I'm going to get rid of the second loop in there. And I'm going to replace this with a comprehension so the function index string returns me a string with indexes on it. And so if I gave it hello it would return me a string that says one H two E three L three L four L five O that's index string. And so this is saying return me the pair of characters the index and the character from index strings when you match that's the scope of the thing when you match the search character by search characters of CH return I. That's not in any particular language here it is actually in list index filter where you take it a predicate and a collection when that predicate is true for the index and element in the index collection when the predicate of element is true then return index is basically the way you read that right. That resulting in a closure code. But let's look at the two versions if you look at the imperative version and look at the number of moving parts. It has 18 moving parts as classes and exit points and branches etc. The functional version has four moving parts so the functional version is much simpler than the imperative version and yet the functional version is more general. The imperative version only works on strings the functional version will basically work on any sequence of things. And so I have here an array of heads and tails captured by these annotations T and H and so I can say index filter give me all the heads. And it turns out that heads show up at two four eight and nine of the positions in that array. Because remember that index filter return me the first match closure automatically turns me all the matches because it's a lazy data structure. And so it can return all of them as easily as it can turn a single one. In fact if I only want one of them I have to say just give me the first return back because it lazily will give me all of them automatically. I can also do something that says give me the first number that generates a Fibonacci number over a thousand and it turns out that's 17 Fibonacci of 17 is over a thousand. That's using the same index filter mechanism. And so the imperative version only searches strings the functional version searches any sequence of values that you can come up with and sequences a core piece of the closure ecosystem. The imperative version only matches characters the functional version matches any predicate that you pass in so whatever condition you pass in is the thing that matches the imperative version returns the first match. The functional version returns a lazy sequence of all the matches. So if you only want the first one you just say give me the first one and it does no more work than it would have otherwise. But then you can keep asking for values and it'll lazily keep giving you all those values if you want. This is a good example of being able to change coded really really granular level and you see this being played out within the closure ecosystem. So there have been a couple of notable additions to the closure world since closure came out in its first release. The first of these were reducers which is basically a way of handling collections more efficiently and more elegantly. Reducers basically allow you to by making a single change somewhere automatically let every single collection in your closure applications become multi threaded. So when you're doing filter map or reduce those automatically go multi threaded and use every processor on your machine. You have to do nothing from a code standpoint to do that because it's built into the language itself. The most recent thing are transducers which is a way of abstracting away operations to data structures so that you can abstract. It's basically uncompleted the operations on data structures from the data structures themselves allowing a lot more portability of behavior across different spaces, different namespaces. So you see this level of reuse being played out in the closure ecosystem all the time. So one last observation about this. A few years ago, maybe six, seven years ago, when alternative languages started becoming really popular, there was a lot of discussion about the characteristics of languages where they are strongly or weakly typed or static versus dynamically typed. My dynamics not showing up for some reason on the pyramid of the triangle at the top. And you could take all these languages and put them somewhere on the spectrum of strong versus weak typing and static versus dynamic typing. I've now come to the conclusion that whether you like static typing versus dynamic typing is more about your personality than any sort of intrinsic capability of the language itself. Because I know a lot of programmers use dynamically strongly typed languages who are incredibly productive and build high quality code and I know people use strongly statically typed languages who are incredibly productive and build really high quality code. I think the much more important distinction now is not about static versus dynamic. I think the important distinction now is imperative versus functional and Java has notably made a strong move over into this functional world. This is really more about the capabilities of the language rather than just the furniture about what the syntax of that language looks like. Back in 2006, I made this observation. This has written alternate languages in the JVM are really coming to their forefront. I wrote a blog entry called Polyglot Programming. It's really this observation that when you have an ecosystem like Java where mixing and matching languages is not the horrific thing that mixing and matching platforms used to be. Mixing and matching platforms is awful. Mixing languages that produce the same backcode is much nicer and easier. My observation here was I think it's wrong to assume that one language can solve every problem. I think we've shown that not to be the case and I give as my example that the swing API in Java, which is absolutely hideous way to build user interfaces. But in a programming world, you can use something like there's a really nice framework in Groovy called Griffin to build swing applications. There's a really nice J Ruby library called Swiby that allows you to declaratively build swing applications and they all produce the same bytecode so you can now mix and match much more easily. After I wrote this blog entry, my colleague Ola Benny made a really interesting observation about this and talked about the pyramid of how your architecture fits together. And his suggestion was you probably want really stable things in the bottom and probably a few number of really stable things and then you can get more and more dynamic as you move up layers. This is mostly in reaction to static versus dynamic languages, but I think this is actually a really good observation. But the stable layer here maybe should be a functional core where you have really strong immutability, really good well defined algorithms that operate on things. Notice that one of the burdens that you have in the world is testing. Testing is really tough. Unit testing is a common practice in a lot of projects. What are you testing when you're writing a unit test? You're verifying that some state got modified from one state to another state. Well, if you have immutability, then all an entire set of unit tests go away because you can't transition something from one state to another if it's immutable. And so your testing burden actually becomes smaller if you have a functional core. As you get closer to the user, this is where imperative languages really shine. Passing state around building user interfaces and things like that. Less critical, but you want really high productivity. This is very much the realm of imperative languages and obj. programming. And then domain specific languages go up and down throughout the entire stack. You see a lot of DSL work in JavaScript now. Link is a great example of a functional domain specific language that operates at the core of your application stack. So I think you're going to see more and more of this style of architecture as we move into the future. And so that is lambda. So when you get back to your office next week, look for places that you're drowning in engineering practices that you've lost the reason why we're doing this. Take a step back and say, is this really the most efficient way to do this? Is there a better way to do this? My day job is as a consultant. And this is phenomenal known as the out of town consultant effect, which says that if you rode on an airplane to get to a meeting rather than on a car, you automatically have more credibility than the people who rode in cars to get to that meeting. This is the out of town consultant effect. And this is a little bit of a joke, but there is some truth here. Because one of the benefits the out of town consultant has is they're coming in looking at all your problems with fresh eyes and they can easily point out like, oh, you know, if you keep jabbing yourself in the knee like that with a fork, it's going to hurt and leave a mark. Oh, I should stop jabbing myself in the knee with a fork. Thanks for that observation. All these little things that you're doing that are kind of silly that have grown up over time. You can point that out. The Pragmatic Programmers had a story in Pragmatic Programmer book about boiling frogs. So apparently if you want to boil a frog, you can't throw them in a pot of boiling water because they'll hop right out. But if you put them in cold water and then heat it up gradually, they'll cook without realizing it. Turns out that story is not true. It's apocryphal because not even frogs are that stupid. But large enterprises are that stupid. Large enterprises are boiling frogs in many situations because they're in these situations where it's hard to trace where you got into this situation. You're just in it now. And so the best thing you can do is be your own out of town consultant. Once you get back to your office, look at everything with fresh eyes and try to figure out, is this the best way we could be doing this? And maybe, just maybe, when you get back to your office for a brief time, you can take flight out of the water that you live in on a daily basis. Thanks very much for coming. Hope you enjoyed it. Thank you. Thank you. Thank you. Thank you. Thank you. Thank you.
 Good morning everyone. This is water. Two young fish were swimming along one day and an older fish  swam up and said, good morning boys, how's the water? And the older fish swam away and one of the  young fish looked at the other one and said, what the hell is water? So this is a little story that's  told by one of my favorite authors in the 20th century, David Foster Wallace in a keynote address that he  did for a university commencement address for graduation speech at a university. And his point in this  story was telling these grad students that the really important things in life are not necessarily  the things you learn in university. It's the way that you interact with other people. It's the way that  you live your life. And he's trying to get them to notice the things that are so common. They're just  like water around a fish and you never even think about them anymore. But sometimes there's real value  in looking at those things with fresh eyes. And that's exactly what I'm going to do in this talk from a  technology standpoint is point out all the places in your world where things are so common that you just  think of them as water. But I'm going to encourage you to take a step back and look at where you're swimming  to see if maybe you could be swimming in a better place. And this is kind of exemplified by three Greek  letters, which is going to be my agenda for this talk. Lambda, mu and delta. So let's start with mu.  Mu, when it's done, used in mathematics, is typically the force of friction. It's a measurement of  friction. So when you talk about mu and physics, this is usually the standard constant they use when they  talk about friction between physical bodies between things. But when we think about friction from a  software standpoint, we think about artificial friction that has been introduced accidentally on  projects by doing things inefficiently because you're using a tool in a less effective way or you're doing  something that seems like a good idea in the micro, but maybe in the macro is not such a great idea. So let's  talk about some common kind of frictions that pop up because one of the things we're trying to do in the  engineering practices of continuous delivery is take a holistic view of all the engineering practices on a  project and make sure they're all working nicely with each other. But let me give you an example of where  projects often fail with this. And it's around this idea of continuous integration. We think about  continuous integration now as a best practice and very, and kind of agile kind of projects. But there  are really two aspects of continuous integration. There's the practice of continuous integration, which  people do as they integrate things, but there's also the machinery of continuous integration. And most of  you, when you think about continuous integration, you think about the machinery, continuous integration  servers that do all this work for you. And you've forgotten kind of about the practice, but I want to bring  this back to the practice of continuous integration, which is exemplified by the extreme programming guys,  basically said, translated in modern terminology, everyone commits to the trunk at least once a day. And this  is still really good advice, but there are projects that accidentally throw this away. And so what this is  really an example of is something that the XP guys, extreme programming guys, identified around continuous  integration and a bunch of other things in the software world. That when you think about the amount of pain  or effort that you have to expend over time, you would think that as your problem gets gradually more  sophisticated and complicated, then the amount of pain that you pay over time for engineering practice  would go up linearly, which seems natural. But it turns out that many things in software, the pain doesn't  go up linearly, it goes up exponentially. And so you hear people talk about doing things, bringing the pain  forward. This is one of the mantras in the extreme programming guys, the observation being for things like  continuous integration, if you do it more often, you end up paying less total cost, and if you put it off for a  long time and then deal with the combinatorial explosion of all those combined elements. In the continuous  delivery world, we talk about doing things as eagerly as possible versus doing them as late as possible.  And so let me give you an example of where people accidentally throw away continuous integration. So let's say  you have a situation where you're doing mainline development on a trunk in version control, but there are  a couple of features that may or may not make it into the next release of your software. One of the ways you  could model this problem from an engineering practice standpoint is to use something called feature  branches. This is the engineering practice. And the idea is to use branches in version control to manage  the speculative in-flight features that may or may not make it into the next version of our software. And so  the mainline is where regular development is still going on. Reverend Green takes one of these in-flight  speculative features and Professor Plum takes the other one, and they start doing work on their branches. Now,  both Professor Plum and Reverend Green being well-established version control citizens know that as you make  changes, you should periodically pull from the trunk because things are still happening on the trunk, and so  as you make changes on your branch, you pull from the trunk and handle those little merge conflicts that come  up. And maybe a few days go by, maybe a few weeks, a couple of months go by, and it's decided we love  Professor Plum's feature. We want it in the next release of our software, and so Professor Plum pushes to  trunk and performs what we refer to as a merge ambush on Reverend Green. Now, Reverend Green didn't do  anything wrong. He came into work on Monday morning, did SVN up, and now slam. He's got a month's worth of  merge conflicts to deal with, which really sucks for Reverend Green. But there's a deeper problem lurking here  as well. What happens if on the very first day of their branch, Reverend Green started making profound changes to  the customer class? And the first day of his branch, Professor Plum started making profound changes to the customer  class in the opposite direction. They've now essentially been working in isolation for a couple of months from  one another, and now Reverend Green has a terrible decision to make. Though I reconcile my vision of customer with  Professor Plum and potentially undo a couple of months' worth of work, or what I'm terrified of, is I'll just  create a second version of the customer class, and we'll let those two things diverge into the future forever.  The failing here is that we've traded one engineering practice for another. We've traded feature branching for  continuous integration, and it turns out it was a really bad deal because we gave up a lot in giving up  continuous integration. And so one of the things that we talk about in the continuous delivery world is you could  solve some of that pain by actually doing proper continuous integration on branches, but we don't want to use  branches at all for this. There's a whole bunch of practices in this world called trunk-based development that  allow you to stay on trunk and keep doing continuous integration and still isolate features that are in  flight or isolate things that are not quite done yet. There are ways to do this. Engineering practices called  feature toggles or feature flags, or the strangler pattern is another way to do this. This allows you to stay on the  trunk and continue doing continuous integration and not accidentally throw it out of the way by picking up some  other engineering practice like feature branches. And this actually highlights a big problem in our world, is that  very often meta-work is more interesting than work. So how many of you would say if you really boiled it down to its  essence? If you had to describe your job to your grandmother that it would ultimately boil down to, well, I take things  from web pages and put them in relational databases and I think things from relational databases and put them on  web pages. So how many would say that that kind of if you boil it down to its essence, that's kind of what you do as  you're living? The problem is that's not very exciting. If you've done that for 20 or 30 times already, that's not very  exciting. You know what developers do when their day job is boring? They invent cool puzzles to solve so that they'll  have cool puzzles to solve when they come into work. And this is where branching strategies end up in most of the  modernization I've seen that heavily use branches. They have built a Byzantine structure of branches that no human  can understand anymore because that's a really good meaty problem that you can sink your teeth into every single day  when you come into work. It's not making your software any better, but it's certainly making your job a lot more  interesting. And maybe that's not the best thing. This is exemplified back from the 90s. Jamie Zwiske wrote this quote  that he referred to as the CADT model of development. CADT stands for the Cascade of Attention Deficit Teenagers  Model of Software Development. And he makes this observation that he was talking about GNOME, the Linux shell. And he  said the problem is every time a new version of GNOME comes out, he posts bugs and those bugs languish for like two  years until they get canceled because a new version has come out that has a whole new set of bugs. And he points out  fixing bugs isn't fun. Going through a bug list isn't fun, but writing everything from scratch is fun because this  time it will be done right. So this is what happens over and over again. This is the water that many of you live in on a  daily basis. So that is friction. Let's talk about change and delta. And math, delta, means the change of something.  But let's talk about change from a technology standpoint. And talk about something that is probably a very common  experience for many of you because when you were very young, you got interested in computers, so you started playing with  computers a lot, maybe computer games, you started hanging out with a bunch of other people who were really in computers.  And probably that was increasingly more and more male, male, male dominated. You went to university and majored in computer  science and computer information systems or something like that. And it was predominantly male. And so when you come out  into the world, mostly what you've been used to dealing with are males, very little diversity. So if you look at this very  famous picture from the first picture from Microsoft, this is Microsoft in 1972, and you'll notice in this picture that they  have three women. Sorry, two women in this picture. One of them is a very, very young Bill Gates. So they have two women  here, which means the ratio on Microsoft in its early days was 11 to two. And unfortunately, that ratio has not changed  much. Here are some studies from Forbes from a couple of years ago, 10 to 12% are female, 17% for women, 7% are  developers were women. This is unfortunately the water that we're swimming in right now is a severe lack of diversity  on software projects. And nobody's really talking about this very much, which is bad. Here's a developer survey from Stack  Overflow that just recently did a developer survey. And this shows even worse that 6% of the women in the Stack Overflow, 6%  of the respondents were women in the Stack Overflow survey. This is bad for obvious reasons because having monocultures is bad,  but it's also bad from an intellectual standpoint. Because of this Harvard study that shows that if you have women on a team of  people who are doing intellectual work, then the presence of women raises the collective IQ of the entire group. It's not that  you've added a smart woman and made the average higher, the presence of a woman raises the collective IQ of the entire  group of people. And that effect works up until you get 50% of women on the project. It has to do with socialization and how  information is conveyed and just a general kind of betterness that comes from having diversity on projects. And notice this is  the collective intelligence of the project. And so if I went to your boss and said, I could immediately make everybody on your  team 10 to 20% more productive with a very simple change that obviously say, well, yeah, that's clear, hire more women is the way to  do that. We need better ways to foster diversity for obvious reasons, but for not obvious reasons too, which is the fact that it'll  make your projects a lot better with a lot less effort because you have more intellectual diversity. So let's talk about another  form of change, some water that you're so used to you don't even think about it anymore. And that's transactions. This becomes a big deal for a  lot of you as you transition from monolithic style architectures to more service or micro service based architectures. Those  transactions are easy when you're in the monolithic world, you got a monolithic application, you got a monolithic database, it's easy to wire up  transactions to happen there. And so that becomes much more difficult when you move to a much more distributed kind of architecture. But this is  actually a huge disservice that we've done to users that trying to get them convinced and transactions are a realistic thing in the world where  they're not. This is purely a kind of artifact of computer science. One of my colleagues several years ago famously made the  observation that your local coffee shop does not do a two phase commit. When you go to your local coffee shop you pay them for coffee and you  get a little slip of paper but you walk to another part of the store and you eventually get a cup of coffee. That's a ventral consistency in a  coffee shop. What would a proper two phase commit in a coffee shop look like? We'd hand them money and they'd hand you a cup of coffee and you'd  let them go at the same time. So that's a proper two phase commit in a coffee shop. So why don't coffee shops do that? Would it not make you feel better as a  coffee member buying in the public if you're coffee shop that a proper two phase commit? The reason they don't do that of course is because it's  screamingly inefficient to do two phase commits in the real world. Everybody's example for how transactions work are banks. But no bank in the  world, in particular no ATM in the world does what we would call a two phase commit for two reasons. One of which they're always designed to  work in offline mode in case their connection to the bank gets severed. They can still operate. But the more pragmatic reason is nobody wants to  stand around in an ATM for five or ten minutes waiting for a query onto some batch job onto a mainframe to tell them how much money you have in your  account. Because you notice that banks still typically do reconciliation in the middle of the night. You know what reconciliation is? It's  eventual consistency. Banks don't do transactions. In fact, banks existed before computers existed and used to bank records traveled on  horseback and there is no two phase commit when there's a pony involved. So we've got to get our users away from this idea that  transactions are a natural part of the universe because they're really not a natural part of the universe. They're an artifact of things we can do with  particular kinds of computer architecture. But we're gradually moving away from the architectures that make that easy and so we're going to have to reconcile this.  And so you hear a lot of emphasis on base and eventual consistency versus transactions as you get more and more sophisticated in the services world.  So this is the asset versus base discussion. You're all familiar of course with the characteristics of acid, atomic consistent, isolated and durable.  Base and a bunch of variations of base are things like basic availability, soft state and eventual consistency. The reason that everyone is eventually moving to  eventual consistency is that as you scale up the number of users that you have and you're forced to scale the distribution of components more and more and more, you always eventually prefer base to  acid because of the cap theorem. This is a mathematical theorem that says that once you reach a certain level of physical distribution of nodes, then you can no longer guarantee all four  characteristics of acid anymore. And so you see really giant, huge code bases move more to eventual consistency because of cap theorem and just because the complexities that have in transactions running around causes on projects.  Another kind of welcome change that has happened over the last few years is the end of the relational database dark ages. We now have lots of options in terms of persistence.  Ten years ago, it was automatically assumed that every project you worked on would be backed by some sort of industrial strength relational database. That was just not even a question.  And so now this is more of a question. So now we're starting to notice the water that we're swimming in and choosing the right side kind of persistence for the kind of problem we're facing.  Another kind of water that you're swimming in all the time and may not realize it has to do with the way your teams are structured. So the very first O'Reilly Software Architecture Conference happened in Boston this last March.  And within the conference, Conway's Law became sort of an inside joke because every single talk mentioned it in some form or another. So it became almost a drinking game within the conference with how quickly is this talk going to mention Conway's Law.  This is an observation that Melvin Conway made back in 1968. It says that any organization that design systems will produce a design whose structure is a copy of that organization's communication structure.  Besides being a little needlessly wordy, what Melvin Conway was talking about here was that from a software standpoint, if you have a bunch of UI specialists and middleware specialists and DBAs running around in your organization,  you will probably end up with an architecture that reflects that communication structure that exists within the people of the organization.  This is what from a continuous delivery standpoint we refer to as a human resources optimized organization. So if you're trying to build the best human resources organization in the world, this is the way to do that.  But this is the water that you're drowning in right now because many of you are trying to build an architecture that doesn't look like this and you're finding it very difficult.  That's because of Conway's Law. And so one of the things that we talk about is the inverse Conway maneuver. Build teams that look like the architecture you want and then architecture will be easy to follow.  Actually, there's another name for this that an enterprise architect came up with and I'll give you this one in case you want to make it sound more official to your management.  So inverse Conway maneuver is one version of this. Domain architecture isomorphism is the other.  It means the same thing, but the second one sounds a lot more important than the inverse Conway maneuver.  If you're really trying to impress a boss or something like that, domain architecture isomorphism is the way you want to go in the future, which is basically just saying build the teams that look like the architecture you want and it's easier to achieve that.  So what that means from a practical standpoint is instead of organizing people like this, organize them in these cross functional teams and very commonly now in the services-based world, you organize these things around the domain entities that you've identified in your code base.  And these cross functional teams will have developers and business analysts and DBAs and operational people, pretty much everything required to make this thing work in an operational kind of context.  You see a lot of interest in shifting team structure to accommodate architecture now, which is a pretty good idea, I think.  Here's another bit of water that you're drowning in from an architecture standpoint.  Because one of the goals of a lot of architects is to create single sources of truth for things. It's just a natural instinct to say, well, we have a lot of information about customers running around this organization.  Let's consolidate it all into one place. We have a single thing that knows all about customer stuff. That's a natural instinct because it's a logical thing to do.  And so we had a client who did this. They run an airline and their architects have put together a really nice state of the art kind of customer service and was backed by clustered application servers and databases and all that good stuff.  And then a volcano exploded in Iceland.  And pretty much everyone who had a ticket on this airline called them up and said, hey, is that volcano going to affect my flight?  And it turns out that people on the other side of the world who had a ticket on this airline couldn't board their planes because the customer service essentially fell over.  Because they built a single source of truth for everything. That means that now customer service calls need to access the customer service boarding needs to access the customer service because they need to look up passenger information through that service.  And so by following their instincts as an architect to build a single source of truth for stuff, what they did was build an operational bottleneck from a business standpoint.  They would have been much better off if customer service and have one piece of customer and boarding and had another piece of customer and other parts that had another piece of customer.  And there might have even been duplication between some of that information in some places, but from an organizational standpoint, that would have been better than having one beautiful source of truth that fell over in realistic kind of, albeit unanticipated, but realistic kind of scenarios.  And so this is a perspective shift that I think architects need to take on that I've been started talking about recently.  That when you look at the jobs that many architects do, they draw lines and boxes and connect things together. This is a really good snapshot of the topology of architecture.  This is a two dimensional view of architecture, which is a valuable thing to have. You need to understand the information flow and how the boxes are all fit together, etc.  But many architects stop here. And that's too bad because this is not nearly detailed enough to be practical.  The next thing you need to do is flesh this out and say, well, the boxcode RM has to be a specific version of a piece of ORM software.  And I have to be able to get that to work in operations with all the other stuff that's working in operations. So you need to flesh this out and make it three dimensional.  This is a 3D view of software.  But this is not far enough either because now if you really want to be responsible for an architecture standpoint, you need to think about, well, a new version of hibernate is going to come out in six months.  And I need to be able to update to that without wrecking my whole architecture in the process.  This is a four dimensional view of architecture. This is the modern perspective on architecture that encompasses not just boxes and lines, but also the operational constraints that you have to handle as part of your architecture.  And how do you evolve that architecture over time?  This is a lot of my personal emphasis right now is working on ways to build better evolutionary architectures that really take into account both the lines and boxes, but also all the operational aspects that it takes to get architecture up and running and evolve it over time.  And so when you look at the problem we've been trying to solve with architecture over the last 15 or 20 years or so, we've been getting better and better at building shared resource style architectures.  This is what model is really good for. Application servers can create multiple thread pools and create database connection pools and make really effective use of shared resources.  But remember yesterday's best practice is tomorrow's anti pattern because a funny thing happened over the last few years. Resources became dirt cheap.  In fact, you can rent resources from Amazon for pennies now. And now our biggest architectural problem is all the coupling that we introduce so that we could share resources and those previous architectural styles.  And the problem here is that it makes isolated change hard. If I need to change the ORM version for the customer service piece of my monolith, but I don't want to change it for the other parts of my monolith, I can't do that because they're all coupled together.  It encourages big bank style deployments. It makes upgrades difficult. Ultimately what this does is make change expensive and make and prevents change.  And which is what we're trying to get away from. And again, this idea that yesterday's best practice is tomorrow's anti pattern.  What we're trying to build in modern architectures like microservices are ones that support change as a first class citizen.  So when you look at a traditional service oriented architecture, this is very much a maximized shared resources style of architecture, which is still perfectly good if that's your main goal is to maximize shared resources.  But when you look at a microservice style architecture, the main goal here is to emphasize easy evolution so that you really do have these kind of Lego components where operationally these things are not coupled to anything else in your ecosystem.  They know how to integrate with all the other pieces, but you can easily remove one of these and replace it with something else that adheres to the same contract and not have to make any kind of ripple changes to the rest of the ecosystem.  And so that's really the goal here in microservices in particular is to build architectures that support rather than punish change.  And that's all about change.  The last thing I'll talk about here is lambda, which as you know relates to functional programming.  And this requires a little bit of a story about me.  When I was back in university, we used a piece of software called pecan Pascal anybody here ever heard of pecan Pascal be careful you date yourself if you say yes to this question.  So the cool thing about pecan Pascal was you could write the same source code in Pascal and it would run on both an IBM PC and an Apple too.  Because they were this is in about 1983, 82 or 83.  And they were using this technology called bytecode.  So when you compiled your Pascal program, you compiled it to this bytecode stuff and then it ran on a virtual machine on the actual computer.  And it turns out it was slower than Christmas because hardware at the time just couldn't really support that level of indirection.  And so this was impractical even for university projects.  But then you fast forward about a decade and you have Java, which uses the exact same kind of architecture.  But now resources are enough to accommodate the sophisticated things that we got with Java.  And this is a trend that we see in software that over time we see more and more busy work to our languages and runtime.  So we gave up memory allocation garbage collection with Java, which is almost all universally a good thing.  You can give up now states with closures and functional programming languages, things like caching, which I'll talk about in a second.  You never have to hand write a cache with a hash table ever again if you don't want to even sophisticated things like concurrency.  Closure does for concurrency what Java did for garbage collection, meaning that in closure you can write really high performance concurrent applications  and never once think about concurrency because the language is handling all those details for you.  And this is a good thing in my opinion because I did a bunch of coding in languages like C++ and Pascal and I reached a really important conclusion.  Life is too short for malloc. The way too low level for me to be concerned with, I would much rather spend my time thinking about the problems I'm trying to solve  versus the plumbing of how all those things end up getting solved.  And that's really what functional programming is about is getting you at a higher level of abstraction away from plumbing.  So let me give you some examples of this. I'm going to start with a quote by Michael Feathers, who's well known as the author of Working with Legacy Code.  He made this observation on Twitter and it's a very acute observation. This is why it's a little bit terse.  And Mr. Feathers makes the observation that object orientation makes code understandable by encapsulating moving parts.  So you think about the things that you know about languages like Java and C sharp.  There are lots of mechanisms to hide information, make it available to some people, not others. Some people can't see it at all, but some people have full access to it.  Those are all moving parts around state. Functional programming makes code understandable by minimizing moving parts.  The theory being if you don't have to interact with it, you can't mess it up. This is like garbage collection Java.  You never worry about memory leaks in Java applications because the runtime just handles that for you.  So that part is just moved away from you so you never have to worry about it.  So let's look at some examples of this. One of the common things that you have in your world are things like design patterns, which is a really useful catalog of things that you can do.  But a lot of these have been deprecated if you have a functional programming language.  So for example, let's say that you have this code. This is the target of a little refactoring exercise. What it does is not that important.  What's important is the observation here that you know what, if you look at this closely, the only two specific bits of code here are these two right here.  And so if I made those generic, I could make this, harvest this as the command design pattern.  And so that's exactly what you do for this. The command design pattern is harvest a command instance and then execute it within the block of this rapid transaction and then create a little anonymous in our class and do all that business to handle that level of encapsulation.  But it turns out the command design pattern is completely not needed at all if you have first class functions in your language.  The command design pattern is really just a bandaid over the deficiency that your language doesn't have a way to pass behavior very easily.  So if you look at the same code in Groovy, which does have closures, of course Java has closures now, you can actually do this with way less formality because this is a feature built into the language.  So first class functions deprecate the command design pattern. You never have to use the command design pattern again once you move to a language that actually has lambdas like Java 8.  This is a great example of eliminating moving parts from you because you don't have to worry about all that scaffolding anymore. The language just handles that level of encapsulation for you.  But let me show you an even broader example of that with memoization.  So here's some code and what it does is not that particularly important. This is actually some Groovy code that classifies a number as either perfect, abundant, or deficient.  And what I really care about here is efficiency.  So in particular, look at this factors of function there.  I want to make that more efficient and actually some of factors is the one I'm going to attack here.  Because what this code does is classify numbers and I may pass a single number in and it'll do is perfect, but it may, if that's not perfect, then it may come back and say, well, is it abundant or is it deficient?  So you may test it more than one time. And so what I want to do is that some of factors where I'm having to add everything up, I want to make it more efficient by creating cash for it and like this.  So again, this is Groovy code. So I create a little sum cash and then if when I do some of factors, if it's not in the cash, then I'll calculate it, put it in the cash and then return it.  Otherwise, you should return that value. So this is using a hash table to create a little cash so that you don't have to do extra calculations that you need to.  Turns out you never have to write one of these anymore because a lot of functional programming languages have this feature called memoization built in.  So as long as some factors there is a pure function, meaning has no side effects outside of parameters passed into it, which is true in this case, I can create an alternate version of some factors called sum of factors where I say some factors dot memoize.  Now some of factors is a function that it will automatically cash values for me just like my handwritten one does.  But this is managed at the runtime level rather than some code that I had to write. This is a classic example of eliminating moving parts.  You never have to hand write a cash ever again because it's now built into the language or runtime.  Now I wrote this example as part of my book functional thinking, which is a book about shifting to functional thinking in the mostly Java ecosystem. And I tried really, really hard to create a handwritten cash that would perform better than the memoization stuff that's built into groovy.  And I failed utterly. You know why? Because they cheat.  They have access to internal data structures and other things that I don't have access to. And that's probably a good thing because I don't understand them deeply enough to really optimize it and make it work well.  But the language designers do. It's just like garbage collection Java. Why are you handwriting something that the language is just handing you in many situations.  So memoizations built into groovy and closure. It's trivial to implement languages like Scala and Java 8. So you never have to hand write something like a cash ever again.  But here's the real benefit of shifting to functional programming. This is the place where this is where it really is like water because you've seen Java 8 come out. It's like, oh yeah, you can do lambdas in Java 8.  Okay, that's cool. Well, we don't really need that. So we'll just keep going with what we're doing now. Here's the real benefit. So let's say that you have this as your company process.  You're given a list of names and you're asked to remove the single letter entries, capitalize the first letter of each one and then return this as a comma separated list.  So this is your job as a developer and you write this in pre lambda Java code and it looks like this. Clean names builds up a string builder and then for each one of the list of names.  If the length is greater than one and capitalize it and append it to the list and then get rid of the last trailing comma that's left over when things are all said and done.  But let's improve this by shifting it to Java 8. So here's the Java 8 company process.  Now I can say names dot stream streaming library in Java 8 allows you to attach behaviors to that stream. One of those things filters out all the entries where the length is not as greater than one gives me the ones that were linked as great as one and then capitalizes all of them and collects all of them together joining them with a comma.  The collectors dot joining with a comma is smart enough to know that you don't want to comment the very end of the list when you're done and so it automatically doesn't put the last one on the list which I had to get rid of by hand in the previous version.  So this now looks very close to the algorithm that describes what I was supposed to do.  Which is nice. This is a way more readable now much more understandable by what's going on.  Let's say then that your boss comes along and says hey you know that name cleaning thing you wrote. Awesome. It's doing a really good job. But we need to work at scale now and you need to make it multi threaded.  Oh great. Now we got to go back and pull that for loop apart. Stop worrying about synchronization blocks and all that stuff in the Java 8 version.  You just convert it to a parallel stream and you're all done. This makes concurrency more or less invisible. Now this is not completely invisible like garbage collection. You still have to understand some things about concurrency.  But you don't have to understand and code every single detail of the plumbing anymore. Most of this is handled for you.  So there's this great thing that the closure community introduced. So closure is of course a really nice functional language. I'll talk more about it in just a second.  But the creator of closure Rich Hickey is also a big word fan and he has resurrected an archaic term which is a really nice term that describes a lot of software.  This term complete things are completed means that you intertwine or braid them together inadvertently. And this is exactly the problem with the pre lambda Java code.  If you look at that code in the upper left hand corner you could make that code a lot clearer if you did three distinct for loops one for loop to get rid of the single letter entries one for loop to capitalize everything and then another one to catenate them all together with commas.  But you don't do that do you. The reason you don't do that is because it'd be really really inefficient to loop through those things all at once.  And so you just complex the problem together into the for loop because that's your main mechanism and you want efficiency. What.  Lambda is in Java eight allow you to do is untangle those things and make a much clearer representation of the problem you're trying to solve. This is the real value of switching to something like Java eight and functional programming because it allows you to operate at a higher level of abstraction  further away from the plumbing closer to the problem you're trying to solve in a language that's closer to the problem that you're trying to solve.  But let's talk at a lower level here because when you look at object oriented languages and the way they want to reuse stuff it's always very big.  So when you look at for example the gang of four book every single example of the gang of four book is a network of classes that interact with one another.  So the common best practice in the object or programming world when you solve a problem in a particularly elegant way you take the piece of that that's really elegant and you package it up for later reuse in a jar somewhere so that you can pass this around and do stuff with it later on.  So in functional programming languages try to achieve reuse at a lower level because in functional programming languages they really want to use pre existing algorithms and mechanisms that have already been highly optimized so for example in the Java world how many XML  libraries are there. There's got to be at least 10 or 12 in common use and probably 50 or 60 of them in total and every single one of them has unique data structures inside them that are completely encapsulated and hidden away which is all fine and good but a very  very coarse level of reuse the way that functional programming languages handle things like XML is they immediately take XML and mash it into a map data structure that they already know how to operate on and they have really optimized mechanism to work on things like maps.  And so code reuse happens in a much smaller more granular level in functional programming languages where you have really highly optimized mechanisms that pre exist for operating on common kinds of things like lists and sets maps etc.  You have data plus code blocks as closures that customize the way the machinery works.  You pass that stuff into the machinery and it passes you back something out that's transformed in some way or another.  So I want to show you a concrete example of this.  I'm going to take something that's probably in your code base for using Java the Apache Commons string utility index of any and I'm going to gradually morph this over into closure code and then talk about the differences.  So here's what index of any does this is part of string utility and so you pass it a string and an array of characters and it will tell you the first care occurrence of one of those characters in the string.  So if you look at the third one up here for example there's no there's no will see shows up the zero position this string B shows up in the zero and two fourth position third position year zero one two three that's the index where it shows up doesn't show up at all there so that's what index of any does.  Here's the code from Apache commas that does that in Java so I'm going to gradually one piece of the time morph this over into closure code and look at what we have left.  So the first thing I'm going to do here simplify the corner cases in the closure world it reacts properly to empty strings and things that are empty or null and it doesn't do anything to them so there's essentially no ops.  And so for the closure version I can basically get rid of those corner cases and so when I have search characters then I need to do this stuff to handle that case.  Now I can get rid of types closure is a strongly but dynamically typed language so you never predefined types enclosures so I can get rid of the type declarations and simplify it down down to there.  Now I need to go in and get rid of this inner for loop right here basically what that's doing is say for each one of these search characters you find one of them return the index where you found that search character.  So I'm going to simplify that say when you find the search character of CH then return the index for that.  And now I'm going to get rid of the second loop in there.  And I'm going to replace this with a comprehension so the function index string returns me a string with indexes on it.  And so if I gave it hello it would return me a string that says one H two E three L three L four L five O that's index string.  And so this is saying return me the pair of characters the index and the character from index strings when you match that's the scope of the thing when you match the search character by search characters of CH return I.  That's not in any particular language here it is actually in list index filter where you take it a predicate and a collection when that predicate is true for the index and element in the index collection when the predicate of element is true then return index is basically the way you read that right.  That resulting in a closure code.  But let's look at the two versions if you look at the imperative version and look at the number of moving parts.  It has 18 moving parts as classes and exit points and branches etc.  The functional version has four moving parts so the functional version is much simpler than the imperative version and yet the functional version is more general.  The imperative version only works on strings the functional version will basically work on any sequence of things.  And so I have here an array of heads and tails captured by these annotations T and H and so I can say index filter give me all the heads.  And it turns out that heads show up at two four eight and nine of the positions in that array.  Because remember that index filter return me the first match closure automatically turns me all the matches because it's a lazy data structure.  And so it can return all of them as easily as it can turn a single one.  In fact if I only want one of them I have to say just give me the first return back because it lazily will give me all of them automatically.  I can also do something that says give me the first number that generates a Fibonacci number over a thousand and it turns out that's 17 Fibonacci of 17 is over a thousand.  That's using the same index filter mechanism.  And so the imperative version only searches strings the functional version searches any sequence of values that you can come up with and sequences a core piece of the closure ecosystem.  The imperative version only matches characters the functional version matches any predicate that you pass in so whatever condition you pass in is the thing that matches the imperative version returns the first match.  The functional version returns a lazy sequence of all the matches.  So if you only want the first one you just say give me the first one and it does no more work than it would have otherwise.  But then you can keep asking for values and it'll lazily keep giving you all those values if you want.  This is a good example of being able to change coded really really granular level and you see this being played out within the closure ecosystem.  So there have been a couple of notable additions to the closure world since closure came out in its first release.  The first of these were reducers which is basically a way of handling collections more efficiently and more elegantly.  Reducers basically allow you to by making a single change somewhere automatically let every single collection in your closure applications become multi threaded.  So when you're doing filter map or reduce those automatically go multi threaded and use every processor on your machine.  You have to do nothing from a code standpoint to do that because it's built into the language itself.  The most recent thing are transducers which is a way of abstracting away operations to data structures so that you can abstract.  It's basically uncompleted the operations on data structures from the data structures themselves allowing a lot more portability of behavior across different spaces, different namespaces.  So you see this level of reuse being played out in the closure ecosystem all the time.  So one last observation about this.  A few years ago, maybe six, seven years ago, when alternative languages started becoming really popular, there was a lot of discussion about the characteristics of languages where they are strongly or weakly typed or static versus dynamically typed.  My dynamics not showing up for some reason on the pyramid of the triangle at the top.  And you could take all these languages and put them somewhere on the spectrum of strong versus weak typing and static versus dynamic typing.  I've now come to the conclusion that whether you like static typing versus dynamic typing is more about your personality than any sort of intrinsic capability of the language itself.  Because I know a lot of programmers use dynamically strongly typed languages who are incredibly productive and build high quality code and I know people use strongly statically typed languages who are incredibly productive and build really high quality code.  I think the much more important distinction now is not about static versus dynamic.  I think the important distinction now is imperative versus functional and Java has notably made a strong move over into this functional world.  This is really more about the capabilities of the language rather than just the furniture about what the syntax of that language looks like.  Back in 2006, I made this observation.  This has written alternate languages in the JVM are really coming to their forefront.  I wrote a blog entry called Polyglot Programming.  It's really this observation that when you have an ecosystem like Java where mixing and matching languages is not the horrific thing that mixing and matching platforms used to be.  Mixing and matching platforms is awful.  Mixing languages that produce the same backcode is much nicer and easier.  My observation here was I think it's wrong to assume that one language can solve every problem.  I think we've shown that not to be the case and I give as my example that the swing API in Java, which is absolutely hideous way to build user interfaces.  But in a programming world, you can use something like there's a really nice framework in Groovy called Griffin to build swing applications.  There's a really nice J Ruby library called Swiby that allows you to declaratively build swing applications and they all produce the same bytecode so you can now mix and match much more easily.  After I wrote this blog entry, my colleague Ola Benny made a really interesting observation about this and talked about the pyramid of how your architecture fits together.  And his suggestion was you probably want really stable things in the bottom and probably a few number of really stable things and then you can get more and more dynamic as you move up layers.  This is mostly in reaction to static versus dynamic languages, but I think this is actually a really good observation.  But the stable layer here maybe should be a functional core where you have really strong immutability, really good well defined algorithms that operate on things.  Notice that one of the burdens that you have in the world is testing. Testing is really tough. Unit testing is a common practice in a lot of projects.  What are you testing when you're writing a unit test? You're verifying that some state got modified from one state to another state.  Well, if you have immutability, then all an entire set of unit tests go away because you can't transition something from one state to another if it's immutable.  And so your testing burden actually becomes smaller if you have a functional core. As you get closer to the user, this is where imperative languages really shine.  Passing state around building user interfaces and things like that. Less critical, but you want really high productivity.  This is very much the realm of imperative languages and obj. programming. And then domain specific languages go up and down throughout the entire stack.  You see a lot of DSL work in JavaScript now. Link is a great example of a functional domain specific language that operates at the core of your application stack.  So I think you're going to see more and more of this style of architecture as we move into the future.  And so that is lambda.  So when you get back to your office next week, look for places that you're drowning in engineering practices that you've lost the reason why we're doing this.  Take a step back and say, is this really the most efficient way to do this? Is there a better way to do this?  My day job is as a consultant. And this is phenomenal known as the out of town consultant effect, which says that if you rode on an airplane to get to a meeting rather than on a car,  you automatically have more credibility than the people who rode in cars to get to that meeting. This is the out of town consultant effect.  And this is a little bit of a joke, but there is some truth here.  Because one of the benefits the out of town consultant has is they're coming in looking at all your problems with fresh eyes and they can easily point out like,  oh, you know, if you keep jabbing yourself in the knee like that with a fork, it's going to hurt and leave a mark.  Oh, I should stop jabbing myself in the knee with a fork. Thanks for that observation.  All these little things that you're doing that are kind of silly that have grown up over time. You can point that out.  The Pragmatic Programmers had a story in Pragmatic Programmer book about boiling frogs.  So apparently if you want to boil a frog, you can't throw them in a pot of boiling water because they'll hop right out.  But if you put them in cold water and then heat it up gradually, they'll cook without realizing it.  Turns out that story is not true. It's apocryphal because not even frogs are that stupid.  But large enterprises are that stupid.  Large enterprises are boiling frogs in many situations because they're in these situations where it's hard to trace where you got into this situation.  You're just in it now.  And so the best thing you can do is be your own out of town consultant.  Once you get back to your office, look at everything with fresh eyes and try to figure out,  is this the best way we could be doing this?  And maybe, just maybe, when you get back to your office for a brief time, you can take flight out of the water that you live in on a daily basis.  Thanks very much for coming. Hope you enjoyed it.  Thank you.  Thank you.  Thank you.  Thank you.  Thank you.  Thank you.
This_is_Water_-_Neal_Ford_0_143.png This_is_Water_-_Neal_Ford_144_191.png This_is_Water_-_Neal_Ford_192_287.png This_is_Water_-_Neal_Ford_288_503.png This_is_Water_-_Neal_Ford_504_575.png This_is_Water_-_Neal_Ford_576_671.png This_is_Water_-_Neal_Ford_672_1559.png This_is_Water_-_Neal_Ford_1560_2159.png This_is_Water_-_Neal_Ford_2160_2291.png This_is_Water_-_Neal_Ford_2292_3407.png This_is_Water_-_Neal_Ford_3408_6527.png This_is_Water_-_Neal_Ford_6528_12839.png This_is_Water_-_Neal_Ford_12840_12971.png This_is_Water_-_Neal_Ford_12972_14207.png This_is_Water_-_Neal_Ford_14208_14411.png This_is_Water_-_Neal_Ford_14412_15287.png This_is_Water_-_Neal_Ford_15288_15899.png This_is_Water_-_Neal_Ford_15900_16427.png This_is_Water_-_Neal_Ford_16428_16943.png This_is_Water_-_Neal_Ford_16944_17627.png This_is_Water_-_Neal_Ford_17628_18827.png This_is_Water_-_Neal_Ford_18828_23219.png This_is_Water_-_Neal_Ford_23220_23315.png This_is_Water_-_Neal_Ford_23316_23867.png This_is_Water_-_Neal_Ford_23868_27503.png This_is_Water_-_Neal_Ford_27504_28511.png This_is_Water_-_Neal_Ford_28512_30599.png This_is_Water_-_Neal_Ford_30600_32363.png This_is_Water_-_Neal_Ford_32364_35567.png This_is_Water_-_Neal_Ford_35568_36683.png This_is_Water_-_Neal_Ford_36684_36755.png This_is_Water_-_Neal_Ford_36756_36899.png This_is_Water_-_Neal_Ford_36900_37151.png This_is_Water_-_Neal_Ford_37152_37751.png This_is_Water_-_Neal_Ford_37752_37907.png This_is_Water_-_Neal_Ford_37908_38015.png This_is_Water_-_Neal_Ford_38016_38183.png This_is_Water_-_Neal_Ford_38184_38519.png This_is_Water_-_Neal_Ford_38520_41567.png This_is_Water_-_Neal_Ford_41568_41867.png This_is_Water_-_Neal_Ford_41868_43451.png This_is_Water_-_Neal_Ford_43452_44423.png This_is_Water_-_Neal_Ford_44424_45611.png This_is_Water_-_Neal_Ford_45612_46247.png This_is_Water_-_Neal_Ford_46248_48407.png This_is_Water_-_Neal_Ford_48408_48455.png This_is_Water_-_Neal_Ford_48456_49259.png This_is_Water_-_Neal_Ford_49260_50627.png This_is_Water_-_Neal_Ford_50628_52847.png This_is_Water_-_Neal_Ford_52848_55367.png This_is_Water_-_Neal_Ford_55368_58283.png This_is_Water_-_Neal_Ford_58284_61907.png This_is_Water_-_Neal_Ford_61908_62411.png This_is_Water_-_Neal_Ford_62412_62843.png This_is_Water_-_Neal_Ford_62844_64391.png This_is_Water_-_Neal_Ford_64392_65219.png This_is_Water_-_Neal_Ford_65220_69275.png This_is_Water_-_Neal_Ford_69276_70643.png This_is_Water_-_Neal_Ford_70644_73223.png This_is_Water_-_Neal_Ford_73224_73787.png This_is_Water_-_Neal_Ford_73788_74003.png This_is_Water_-_Neal_Ford_74004_76139.png This_is_Water_-_Neal_Ford_76140_80742.png