 with Michael Wood, and please send us your phones and if you tweet, use the hashtag. After this is the last section of the day. After this, just to remind you that there is a social event at the something brewery, you know what they said. Check your local listing. And good luck on it. All right. Appreciate it. So there's a microphone. I might try to use it here. I'm told everybody's got a fresh bag of chips. So I want to make sure you can hear me. Freakle the volume. Right? So yeah, so my name is Michael Wood. Twitter handle and website are WP Scholar, essentially. So you can go there. So this talk is Basic Principles of Software Architecture. And so I am a WordPress developer at Bluehost. And I am a designer. And I know this is a dev talk. You're thinking you're in a dev talk. But I'm actually not really a designer in the way you're probably thinking. Most people don't think of developers as designers. And of course, I'm not talking about designing graphics or some sort of thing like that. I'm basically talking about designing software, right? So that's one of the big things that kind of sets a good developer apart. Is their ability to design software as opposed to just build it, right? So that's what we're here to talk about today. So this is what the standard software development lifecycle generally looks like. We start with requirements. What is it that we want to build? And then we have the design process, which is how should we build it? Then we would implement it. Then we would QA it. And then of course, we would have to maintain it. The problem is, a lot of times, at least until you get to where you have a more mature process, most people will just run across the top here and forget the stuff on the bottom, right? So they do the requirements. They go ahead and start building because they obviously know the solution. And then they go on and they maintain it from there. And they skip the design because that eats up too much time. And they skip the quality assurance because somebody's gonna notice it, right? And then you'll fix it. So why do I need to spend my time when I can have all the sites visitors figure that out? So that's kind of the way that normally, until you get some mature processes and you start to actually put design and put QA into the process, you just kind of run across the top. And so of course, what that ends up doing is, well, of course, I don't know, how many people have ever been in a consultation with a client before you actually mature, I guess, as a developer and the client says, oh, this is something we want to do. And then you're like, okay, and this is the solution. And you've already thought of the solution before you've even finished hearing the requirements from the client. So obviously that's the kind of thing we wanna avoid and we wanna make sure we actually design. So the focus today is specifically on that design aspect and thinking about what we're gonna do. And of course, you can say there's a lot of things that you can design and a lot of people think about software architecture from a systems standpoint in terms of, I need this server and I need this load balancer and all this kind of, so we're not talking about that. We're talking about code itself. How do we fit the pieces of the code together in a way that makes sense and a way that we can reuse them? So first question we have to ask ourselves is what does it the clients actually care about? So obviously the client cares about cost. So if we can't keep costs at a reasonable level, they're not gonna be happy. Timeline is very important. If you can't deliver something within a reasonable time, actually a lot of times this can be the death of a project, right? You take too long, they just give up on you and go to a different developer, but you've already almost finished the project. And then effectiveness, right? So they have some sort of problem and they need some sort of solution but not just any solution, one that actually solves their problem in a reasonable way. And the better that it solves the problem, the better the solution is, right? So those are kind of the three big things that you look at for that. So when we're looking at how changes affect timeline and cost, I think it's pretty logical when you look at it that if you make a change in the design phase before you've actually written any code, it should be fairly easy to make that adjustment. But then as you started writing code and maybe you've gotten halfway through and they come back and they say, oh, we wanna change this, say, okay, great, we haven't shipped it, whatever, we can make that change, but it's gonna take a little bit more time because now you have to undo some code, maybe not as much as you would have. And then you have to obviously spend that extra time on it, which obviously is gonna increase the cost. But if they come after you've completely delivered the thing and they say, you know what, this isn't exactly what we had in mind. So we'd really like to kind of rework the whole thing, you know, not overdo the whole thing, we just need to tweak these little pieces that have fundamental code changes that will affect everything, but I don't realize this because the client never knows those things. Then of course, that's pretty much guaranteed to cost a whole bunch of money, be a whole lot of hassle and really throw the timeline off. So obviously if we can avoid these kinds of situations, we can save the time and the money upfront if we can make our changes early on in the process, which means having a good design of the system and making sure that we address all those kinds of issues and problems and really run everything by the client in a way that they understand before we start. So then really we boil down to what actually makes our solutions effective, right? So how do we make sure that we meet the effective portion of the client's requirements? So one, obviously we have to solve the problem, but it's not just about solving their problem, it's also about making sure that what we build is actually easy to maintain because if we build something that costs them more money in the long run to maintain, then guess how long your code is gonna stick around? Not very long. You wanna ditch your code, move on to another developer, continue the cycle and not really get what they need. So it's our job to make sure that we don't just solve their problem, but we make sure that in the future that we reduce costs and make sure that we don't screw them over, basically. So this is a simple maths problem, right? So there's no code in this presentation, but there is maths. So basically this is a pretty cool formula, essentially. And if anybody here wrote the book, read the book, Code Simplicity, everybody should go out and buy the book, Code Simplicity. It's a really good book. Some of the stuff I'm covering today actually is from the book, but this is essentially the equation of software design, right? And so all these letters mean different things. Essentially the D on the far side is the desirability of a change or a feature, right? So all of these factors over here are gonna affect whether it's a good idea to actually implement a particular solution. And so we have, it's kind of hard to do, I wish I could have, I guess I could have laid it out a little better. We have a division, right? So on the top we have these two V entries and on the bottom we have these two E entries. So the V stands for value and the E stands for effort, right? So if at a basic level, value over effort is what would result in the desirability of a change. But it's not just the value of something and the effort that we put into it. The N over here stands for the value of something now, whereas the F is value of something in the future. And then the E with the I next to it here is the effort of implementation and the M stands for effort of maintenance, right? So we can have a problem and we can solve it and if you look at it very simplistically, you're looking at the value of what your solution offers and the effort that it takes to do it. But if you look at it holistically, you're looking at the value that it offers us now, the value that offers in the future and the effort it takes now as well as in the future. And what you see a lot of times is as developers, if we don't plan ahead for the maintenance and make sure that our code is not gonna get harder to maintain over time, but actually hopefully easier or at least not any more difficult than it already is, then essentially what will happen is assuming we provide value into the future, the value would overcome the maintenance costs and what we build will be useful and have longevity. So that is the basic idea. So of course if we don't meet that and we choose the easy, short, dirty solutions now, then we're gonna have to deal with the maintenance costs later and that's what we call technical debt. So if you ever had a credit card and you pay the minimum fees and you have 24% interest rate on your cards, you know very quickly that you will be in debt and unless you make some changes, you will continue to be in debt and that's exactly the way it works with software. If you don't deal with your debt, then you're gonna continue to have issues and essentially probably go bankrupt in the sense that your project will never see the light of day or get trashed quickly and your client will move on to somebody else. So there's the law of change which basically says that the longer your program exists, the more likely that any part of it will have to change, right, which is normal, right? Because if you have a working system, there are also business plans and marketing strategies that go along with this and those things are gonna evolve and change as things become more or less effective and so you have to adapt and make sure that your software goes along with those things and if you don't plan for change, then essentially you are guaranteed that you're gonna become more and more in technical debt. So you wanna make sure that you plan for that change. There's also the law of defect probability. So basically this is saying that the larger the change you make in a system, the more likely you are to break it or to cause some sort of defect, right? So the more code that you have to change in order to fix a bug means the more likely you are to introduce more bugs into the system. So it's kinda like I have one bug and I go in to fix it and I have two bugs, right? So if you plan and strategize in a way where if you have an issue you can consolidate that the need for change to one specific area, then you reduce the chances that you will have some sort of defect as a result. Now obviously if you do quality assurance you can catch these things hopefully before they get into production but that's not what this part is. Hopefully you went to Russell's talk and learned all about that. But essentially yeah, the law of defect probability, the smaller the change the less chance for breakage. Which is also if you do go in and make a code change trying to minimize as much as you can if you have two choices always try to touch less code. Then we have the law of simplicity, right? So the ease of maintenance is proportional to the simplicity of its individual pieces. So the more complicated you make your code obviously the more complex and difficult it is to maintain. That's pretty obvious. But that's essentially key when you're designing a system, right? You wanna keep it as simple as possible to make it easy to understand and maintain. And understand is a very big part, right? We spend a lot of our time when we encounter a bug and we have to go change code while the first thing you have to do is you have to read the code, make sure you understand what's really going on with the code, right? And if the code is difficult to understand then it's gonna take you a lot longer to figure out what's going on which is gonna take more time, more money for the client and again it just adds to the complexity of maintaining a system but you also wanna make it easy to modify. So even if it's easy to understand if you have to change code in 15 different places it wasn't necessarily easy to modify and of course then we're dealing with the law of defect probability, right? We're exposing ourselves to the chance for more defects. So ideally we wanna make our code easy to understand, not have to grok the entire system but merely just a part of it in order to be able to modify just that part hopefully so that we can get in and out with the solution. And of course we wanna make sure it's easy to test. So we wanna make sure that what we expose to the outside world is easy to interact with and make sure it works correctly. So reliability does not equal understanding. So when I say understanding a lot of people are like, oh yes we need coding standards and blah blah blah so that everyone can see the code and make sure they understand it. And code standards are great. Linting tools are great. All these things that help you make it nice and clean and kind of consistent is important. And of course making sure the code has consistent structure and that kind of thing is important but your coding tools don't do that for you, right? So making it more readable is not gonna help anyone understand your code. On the other hand, if you have clear naming within your system it is going to be self-documenting which is gonna make it easier to understand. So naming variables in ways that make sense but also help expose what's going on. So a lot of times if we write a conditional, right? We say, oh if number 39 is greater than x, then do this, right? Well, somebody looking at that code, they say, oh what in the world is 39 and why is it so special? Whereas if you'd name that value specifically then it would become very apparent. For example, if we said, or let's say it was 42. The meaning of life is greater than or something, right? You have some sort of context into what that conditional means, right? So making sure that you not just name your variables correctly but you have clear and consistent ways that you name your classes, the way that you name your modules or packages. Everything should be coherent and make sense. Somebody shouldn't have to learn a totally different way of working when they're moving around in your code. As we said, so we're gonna run through some acronyms that hopefully everybody's seen but keep it simple, stupid. Again, simplicity is very important. Dry, I'm sure everybody's heard this, don't repeat yourself. So if you've got code that you've used in one place and you find yourself needing to use it somewhere else, don't copy the code. This is the number one saying that I think beginner developers make for sure is that they're just quick to copy code. Move it around. But even as a more advanced developer, it's tempting that you've got some sort of package or module of things you put together. Maybe it's a plugin or something and instead of putting it in a repo and then reusing it and versioning it, you find yourself just copying it over. And then on the next project, you copy it over and then what you end up with is you fix the bug on this project and fix a different bug on that project and you get ready to start another project and now you've got three copies of the code. Some have bug fixes here, some have bug fixes there. Now you don't know what you fix where and so now you're using outdated code on a client site. So making sure that you standardize how and where you put that stuff is important. Yagney, yet another, which is not what it stands for, yet another problem that we run into. Yagney stands for you ain't gonna need it. So another kind of mortal sin that we all make is trying to be creative geniuses and write code that you know the client's gonna want but they haven't asked for it yet. And so it ends up being one of those things where you implement it and it sounds like a great idea and it sounds like you're thinking ahead and being smart but 99% of the time they come back and they say they want something similar but it's totally different. And so all the code you wrote is not useful or maybe they didn't need it and then you wrote all this code but now your next change means that you have to actually change this code that's not being used or some other developer has to come in and change this code that's not being used and they don't understand what it's for because it's not being used. So all it does is add more confusion than help. So anytime you add stuff like that, especially if there's multiple people on the team, you're just increasing the chance of misunderstanding and you wanna make sure that you obviously keep it simple and don't do that. So we'll run through a few coding principles here. So this one is the single responsibility principle which basically says that software entities should have only a single responsibility and should avoid side effects which basically means, for example, if you're looking at a system as a whole you should be able to clearly identify sections of responsibility, right? So if we look kind of in a more classic, here's the user interface, I have some business logic that kind of works in between and then we have the database and where all the information gets stored. These are all different layers or responsibilities in the system and they should be separated so that we can keep those apart. But then as you delve in, you can say, oh well, within our business logic we have this particular process, that particular process and then we would wanna split out into modules. We want these different processes separate and then as you delve into the modules, we have classes that handle specific responsibilities so you wanna make sure that you're not putting things in there that aren't coherent and then of course even at the functional level you wanna make sure that you keep those things nice and clean. And when we say side effects, a lot of times, for example, we'll have some specific function or thing that we're trying to call to incur some sort of change, right? So maybe we just wanted to update a meta value in the database. So we would just wanna store a username or some sort of ID or something like that. So if we have a function called save ID, right? We'd expect it to save that ID but when that ID also happens to cause an email to get sent, then we've caused a side effect and so we've put too much responsibility within that particular function. So as you're looking at your code, you wanna make sure you can identify those clear boundaries for all your code. So then we also have the open and close principle which basically says the software entities should be open for extension but closed for modification. Basically WordPress does a really good job of this. WordPress core is closed for modification. I mean, some people obviously go in and hack WordPress which is not a best practice at all and they'll change files in WordPress core. But in a standard install, right? Nobody can actually easily change WordPress core, right? You just get WordPress core and then we have plugins and we have themes and all of these things are separate entities and they operate on their own and it's very easy for a plugin to extend WordPress by hooking into all the available hooks and things that WordPress provides but at the same time, it's closed for modification. It's not very easy to externally modify WordPress itself outside of the system it provides for you. So obviously when you're writing your own software, you wanna make sure that as much as possible you lock it down but provide reasonable ways for it to be extended. And of course what this is gonna do is make sure that external plugins or whatever your external entities are can't change your system in such a way that would completely break it, which is the main idea. So the key here for all of this is really just modularity. And modularity of course applies at many different levels. So it's good to kind of think about it more in the macro and the micro level. But there's a few principles when you're thinking about modularity that you wanna consider so that you can make sure that you are properly packaging things. So the first one is decomposability. So it should make reasonable sense that making something into a module is decomposing the problem into smaller pieces. We don't wanna take something and break it down so small that it no longer makes sense. Because it's when we do that that it loses all meaning. So we wanna make sure that it's easy for us to understand why it would simplify the system if we were to decompose it and pull it into its own module. At the same time it should make sense that we should be able to compose those modules and put them together. So if it doesn't make sense that this module that we created, if there's no easy way to plug it into a system and make it easy to add that feature or thing on and then be able to plug and play like Legos, all these pieces, if the module you created, well maybe it makes business sense that it would simplify some logic or something. But there's no practical way to plug it back in and build something or build multiple things with it, then maybe it's not right that we put it into a module. So both of those are two rules that you wanna kinda look at when you're trying to figure out what should go in a module and what shouldn't. And the other is understandability, right? So the whole idea behind modularizing, well not the whole idea, there's many ideas behind it. But basically you wanna make sure that you're making things easier to understand. One of the biggest enemies I guess of a developer is going into a system and having to delve through 15, 30 files, navigate several directory structures, figure out what this class is and what that class is and then realize that the breadth of the system and the way that it interacts with all the pieces is very expansive and that you can't solve your current problem without understanding and keeping in your mind all of those pieces at the same time. So you wanna make sure that when you're creating the module, if there is a problem, it should be very clear that it's very likely this module and if I could just wrap my head around this one module, then I can understand and get a better idea of what's going on and how to fix it. And the other one is continuity, right? So after you've created a module, continuity is essentially the idea behind, if I do have a problem, right? Some bug does come up and I need to go solve it. Is it broken down in such a way that when I do go to solve it, I really only have to edit or fix one particular module? So again, same idea. If you have to touch a whole bunch of different modules to solve a problem, then it's very possible that you haven't properly packaged things. You haven't properly modularized. So you wanna make sure that if you realize you have a database problem, you shouldn't have to touch anything except your database module. And it shouldn't bubble up into all the other modules and cause you to have to make changes. There shouldn't be such heavy dependencies between the pieces that one module breaks another and so on, which comes to the next point, which is protection, right? So protection basically is saying that if you do have a problem, some sort of abnormality during runtime, that essentially that error or runtime issue stays contained within the module and doesn't cause all the other modules to break and go, right? So you should have some sort of error checking, some something to contain the issue to that module and to keep it from bubbling up or out, per se. So as we can see modularity is important and kind of these five rules here of decomposability, composability, being able to take it apart, put it back together and it still works, then you're good. Again, the whole goal of simplicity, making sure that you can understand what the module's doing, how it works, how it interacts with other pieces. So obviously we've talked about what it makes sense to go into a module, but at the same time you also have to consider how the modules themselves communicate. So you wanna make sure that you expose reasonable APIs within your module and nothing more than should be, right? It shouldn't be necessary for module A to know about all the internals of module B, it only needs to know the things that module B makes public. So if I'm trying to create a, for example, a WordPress post expiration module, then I don't need other pieces to know about all the cron jobs that are set up or all these different pieces. I only need to know set an expiration on this post or unset an expiration on this post and that's your public API. That's the only thing that any other module or piece would need to know about and if there's some sort of error, then those functions should give you an error. The module that's using it should be able to detect that and then your error stay contained. So then we have the reuse and release principle. So basically what this is saying is that only components that are released through a tracking system can be effectively reused, right? So the idea behind the Lego is you have blocks and you put those pieces together and you can take them apart and you can use them again, right? With software, we package things up, we have a plugin and we release it. Because we release it, other people can use it or we can use it again and again on other sites. If we don't have a method of releasing it and tracking it, which when I say tracking, really think more about semantic versioning, some sort of versioning system, where we have version one of a plugin, version two of a plugin, that kind of thing. So if you don't have some system of tracking and releasing, then there's really no useful way to reuse that code because your only way of actually using it would then be to resort to copying and pasting, which as we know is just gonna lead to you copying and pasting more, changing some things and then not knowing what code to use. Whereas if you track that code and you release it through a system, then you can have one copy of the code and you can use version one here and version one here and then you can fix bug in version one and you make version 1.0.1 or whatever it is and then over here you can actually update the version and now you have all your stuff run in the same code. So it's important principle and this is why we have things like Composer and NPM. So Composer if you use PHP is a wonderful tool. If you're not familiar with it, you should look it up. It has a lot of the same features if you're familiar with NPM where essentially there is a repository of code that you can use that other people have released and you can simply run Composer, require some package of code and that will get pulled into your project and then you could start coding and writing against that code and kind of gluing all the pieces together and of course you can release your own packages as well so that you can then release and reuse your code through some sort of track system. And so just like that, you also have NPM so if you have JavaScript packages and things like that, you can do the same. If you have CSS even, there's a, remember, Bower, yes, that's the thing. So yeah, so all these different tools that are available so you can release and track and reuse your code. Even if you're not using these tools just a simple repository where you can tag versions is going to do the trick but you have to have some system for doing that. So there's a few resources I'd recommend you check out. Obviously I've been addressing more kind of the higher level why and how of packaging things and not the specific design patterns or things like that that you might actually implement in your code but it's important to actually realize that above just knowing the syntax and the language and conditionals and all those functions that exist in the language, there are also design patterns which are widely accepted as a kind of best practice for designing and putting these systems together even within the context of a package or a module that you might release, structuring your code even better. Sourcemaking.com is an excellent resource. It basically will show you a whole bunch of design patterns as well as anti-patterns. So things that you should avoid doing or kind of code smells that you would find in your code and to kind of give you a better feel for maybe what you're doing right and what could be improved. And they also have, so I think most of their examples, they have a number of different languages that they give the actual code examples in. They have PHP, Python, Java, Delphi, a few others but they don't do JavaScript examples so I'm putting a JavaScript design patterns reference there so you can kind of see some examples if you're more on the JavaScript side of things. And then there's four books here that I recommend. Everybody check out. Clean Code by Robert Martin is a great book. It talks a lot about naming conventions and all the things that really just kind of make your code nice and crisp. And then Code Simplicity is a book. It's a small book so if you're not a big reader, I'd recommend you start with that one. It's not very thick but it's very straight to the point and really illustrates some of the concepts that I've been talking about here as well as additional things. And essentially this guy was on the Mozilla project I believe and so it got to a point where developers were just, it was like a revolving door. They'd hire somebody, somebody'd come in, they'd be like, yes, I'm gonna work on this and then they'd see the code and they'd just leave. It's hard to maintain. It was just such a beast that it got to that point where they couldn't keep developers on the project at all and so he was able to come in and really just focus on simplifying, simplifying, simplifying until they get to a point where it's actually easy to maintain or at least moderately easy to maintain again instead of just this behemoth. And then this refactoring book, Robert Fowler. So if you have existing projects and you do need to go through that process of simplifying and kind of breaking things down and just cleaning up old code, that's a great book. And then another one, PHP Design Patterns. It's kind of one of the books that I read that kind of familiarized me with the concept of design patterns in general. And obviously the examples don't just apply to PHP. There's many languages. The patterns are not language specific. Usually if there's a pattern, you can figure out a way to implement it in whatever language you're working in. So all of those are excellent resources and I recommend that you check those out. And I think we have about 10 minutes left or so. And so if we've got questions, yes. Just curious about your opinion on Git strategy. So let's say the WordPress environment or the WordPress project, you've got Composer all in, Auto install is, and you've got your own custom code. Do you think that the work you're doing, let's say it's like custom client work, for instance, do you think that belongs in their own separate repos and then you install those pieces separately or basically how's your getting in on file? Right, yeah. So yeah. Any questions for the video? Yeah, so for the video, it's a long question. I'm gonna do it word for word. Here we go. Yeah, so in general the question is, with all the bits and pieces that you can release as packages, how does all that come together in an actual project essentially so that when you're trying to version control things and get ignore things, what actually ends up in the project, especially particularly pertaining to custom client code, right? And so what I found in the beginning of my development is that I would try to modularize, but I would miss a lot of opportunities to do so because I just, you know, you kinda have to train yourself after a while to learn how to package things and this is slightly off topic, but really, really, I think this is a, you know, hits home to me because I talk about modularity and a lot of people are like, oh yeah, yeah, I know that's important, but no, no, really, modularity is important. But yeah, so a lot of what I do, anything generally, anything that is in versioned via Composer or NPM or any of these other tools, it's obviously third party code, even if you wrote it, right? Like you don't wanna physically change code that you have versioned in some system somewhere on a client and not push that back and then update your version in that project, right? So it's important that you just kinda get ignore all those things so that the code doesn't actually live in the repo, so nobody can actually change it there and that prevents, particularly when you're on a team, other people coming in and actually being able to push that code to the repo and not follow best practices, so that's important. Client-specific code, particularly with WordPress, if it's a theme and it's design-focused, it all goes in the theme. I always try to have a client plug-in, essentially, that's specific to the client and whatever custom code or kind of glue for all those other pieces goes in there. So that's kind of the way that I usually do it. So as far as a WordPress project, we're looking at WordPress is in its own directory. It's pulled in probably via Composer. You've got plugins and themes that can be pulled down from WPackages and pulled in. You've got, I've got a number of custom modules that I've created for WordPress that I usually will pull in most likely into the plug-in that I write for the client and then from there, yeah, anything you do beyond that is, which should be relatively small amount generally, goes in the repo. So there are times where sometimes it does make sense to commit the vendor directory depending on deployment processes and just simplicity for deployment but as a best practice, keeping that out keeps people from, especially on a team from pushing things they shouldn't push. So yeah, any other questions? Assuming I properly addressed that one. So, modularity is easy, right? So, yeah, so I'll, I'm always happy to talk about it. So I'll be at the after party or the Bluehost booth tomorrow or wherever. So feel free to catch me and ask questions or chat or whatever. But yeah, so I guess we'll just finish a few minutes early. But thanks. Thanks.