 Hey, it's time. Yay. OK. Hello, everybody. Thank you for coming to my session. I'm really excited to share the way that I manage sites with you. This session is particularly about how to deal with configuration management without using the features module. My name is Jen Lampton. You guys might know me from various other places in the Drupal world. I've been one of the main organizers of the Bay Area Drupal Camp in San Francisco area in California since about 2007. I was the initiative lead to get twig into Drupal 8 for about two years mid-Drupal 8 cycle. And more recently, I am infamous for being one of the co-founders of Backdrop CMS, which is the Drupal fork. For those who don't know me, as I said, my name is Jen Lampton. I've been doing Drupal for about 11 years. I'm one month away from my 11th Drupal birthday. It's really exciting for me. I still love Drupal. And something I think is a different from a lot of maybe other people who are at DrupalCon is that I do not work for a Drupal agency. I'm an independent contractor. I find and manage my own clients, my own jobs. And that changes the way that I do Drupal work. Usually, that means I'm on smaller teams. Usually, one to four developers, oftentimes I'm the only developer working on a project. That often means I'm working on projects that have smaller budgets. So sites as small as $10,000, usually the highest site I'm willing to take on is something around $75,000 US. And projects with budgets smaller usually mean smaller sites too. So it's a different kind of a world than if you're working at a big agency on big projects with lots of dollars for development. This is an example of the largest site that I currently manage. It's for California Government Agency. And I wanted to give you this as an example because at the end, if we have time, I can show you what their code looks like using this model. But this site has two active developers. There's myself, and we've got a front end around the project. We have 10 active editors, so these are people who are creating and managing content on the site. There are about 100 contrib modules on this project. So it's got a lot going on, even though there's not a lot of people currently working on it. And this site is built and managed in Drupal 7. So I know we're two years into Drupal 8. But let's talk really quick about Drupal 7. How many of you are currently running or managing Drupal 7 sites today if you could hold your hands up? OK, that's fantastic. That's all of you. Keep your hands up, keep your hands up. OK, so for those of you who have your hands up now, how many of you are using the features module to deal with configuration? That's what I thought. And those of you who are using the features module on Drupal 7 sites, how many of you had any trouble dealing with the features module? Right, that's like everyone, so that's fantastic. I am hoping that this talk is going to be really relevant to you guys. I have experienced a lot of personal pain, and I've tried to find a way around some of these problems. And I think I've developed a strategy that is working really well for me and my tiny teams. So really quick, let's talk about features. Features is a fantastic Drupal module. This session is not a features-hating session. I really like features. I just think that it doesn't always work great. There are two really strong use cases for using the features module. The first one is for bundling functionality together. So a good example, or the second thing features does, and this is something I think most of us use it for today, is for dealing with configuration management. This is a solved problem in Drupal 8. It's a solved problem in Backdrop CMS, but not a solved problem in Drupal 7, which is why I think everyone's using it. But first, let's talk about the first use case for using features module. And this is, for example, if you have a site that has a photo gallery, that photo gallery's gonna have a content type, it's gonna have a view, it might have a panel, there might be specific view modes or views displays, or other little bits and pieces that you need to manage to create that photo gallery. In a perfect world, you could build the photo gallery once, and then you could bundle it together, and then you could sell it to a second site or a third site, or you could distribute the same thing across multiple sites. I think this is really valuable if you have a really big, complicated site, or maybe a multi-site, or you have an institution that has requested the same thing in multiple places. But in my experience, doing smaller sites, the photo galleries I build are never the same from one project to the next. I have only ever in my entire time using features needed to feature exactly the same thing to distribute onto a second site. So that's something that maybe isn't super relevant for my use case. The second thing that features is used for is for configuration management, and this is to make a better developer workflow. And that's because if you have a view that's created for your photo gallery and you need to make a change to it, maybe you need to add some new module that goes along with it, you wanna deploy the new module and the change to that view at the same time and have them appear on your live site. And there isn't really a great way to do that in Drupal 7. Features came along and provided this awesome tool that does that mostly. And so everyone has kind of adopted using features to solve this problem. I would argue that this is the more common use case for what people are doing with features today. So both of these things sound really great. Like you're like, wow, I want both those things. I wanna be able to bundle my stuff together and organize them neatly. And I wanna be able to solve configuration management. But I have found that neither one of these use cases are super relevant or effective in the kinds of work that I do on a day-to-day basis. So as I mentioned before, I've only ever delivered exactly the same feature once. And that was two sites for the same client. Usually what makes somebody's Drupal site or website at all great is the fact that it's not exactly the same as someone else's, right? Like what sets you apart from your competition? You've gotta do yours a little differently. It's gotta be specific to your content. It's gotta be specialized to what you needed to do. So discarding that use case. Use case number two, configuration management. I've found three major problems in dealing with features on my Drupal seven sites. The first one is performance. I find that when you have the features module on your site with a lot of features, that module is constantly checking to see if your current features are in an overridden state and that can slow down loading unrelated pages. And I find that super frustrating. I also find that it often takes me more time managing the features than it does to do the change the features are supposed to be managing. So if I need to make a change to a view, changing that view is really fast. That's like a five minute thing. But if I need to then like get that view into the feature and then get the feature onto the site and then get the site to revert, that's like a half an hour process. And like now taking so much longer just to manage the configuration for the change than it would have taken to just make the change on the live site. Just a good thing necessarily, but just something to keep in mind in terms of how much time it takes to accomplish your goals. The third thing is developer sanity. And this is something that I find particularly important if you're like the only developer on a project. You run into one of those little things that features isn't necessarily really good at managing. Like somehow you have a view that has a path and ends up in a menu system and you're trying to get the menu to revert and like your menu system's getting all broken. It's just the amount of time you have to spend fixing that one problem is so much more than it would have taken to have done the change initially and you end up getting really frustrated with the system and trying to figure out like what is a better way that can keep me from getting to this place of complete frustration in the future. So those are the things that bother me the most about using features for configuration management. But I wanna do say that this is not the fault of features module. Features module is the best tool we have for this in Drupal 7. It is really good at doing a lot of things like giving a user interface for managing things that you wouldn't be able to do without that. And everything that features does really well, it does really well for a reason. It's because Drupal does those things really well and the things that it doesn't do really well are Drupal's fault, right? Drupal does not deal with deploying menu items or blocks or anything with a unique identifier really well and features is trying to give you a way to manage those things and that it can do. It does what it can do and it's working with the system it's got. So I would argue that the kinds of problems that I'm facing with features modules are actually particularly hard on smaller sites with smaller teams. I think these are things that are easier for big teams with bigger sites to overcome and I just wanna talk a little bit about why I feel like maybe I feel that pain more than others. So for starters, performance is something I've forgotten in but for performance is something that I think is a little harder on smaller sites because smaller sites don't often benefit from fancy Drupal-specific hosting that might have lots of different caching layers. So if for example you're depending on that page cache as being like your primary cache and that cache needs to stay primed in Drupal 7, it's not gonna be doing some kind of background fetch in order to make sure that cache is up to date, then if you have to clear your cache, like that's gonna be a hit to anyone who comes to your site after that cache is cleared. So you generally don't wanna be waiting for things like your features to find out if they're overridden or not after you deploy some changes. Developer time is also something that's a lot harder on small sites because these companies that are paying for them usually have smaller budgets and so if you overshoot by something that should have taken five minutes is now gonna take half an hour an hour, that's a big difference to those people maybe they now can't do the features. They can't add things to their site that they might have been able to afford to have you add if you as a developer are doing things like managing configuration with that time instead. So when you overshoot on how long something takes, it affects the scope of that project, it affects the budget. Whereas if you had a huge budget an hour might not be a big deal. And about sanity, I don't actually think it's more important for developers working on small sites to say sane. I think it's important for all developers to say sane. So that's just something that is a particular pain point for me trying to get my features to revert. And I think everybody probably feels that same pain. So I would like to show you another way but this is very specific to the use case that I've outlined with smaller sites, smaller teams. It's something I've had a lot of success with. It might not work for everyone but I'm gonna show you my code and see what you guys think. Before we get into the code I wanna talk a little bit about how Features Module itself works. So we have Features Module is a contrib module that means it's using Drupal's APIs. Those APIs provide you a way to ingest things from code. That's what Features does, it exports to code and imports from code. If it's using Drupal's APIs to do that Drupal APIs also give you a way to export and import things from code. A lot of those APIs give you not only a way to have a default thing but also to override a thing that might already be provided. Anything that Features Module is using Drupal APIs to do is something that Features Module does really well. So something like use exports. It's a great example of something that Features can deal with without any problems. There's other things too. If you had a custom module you could use the same APIs that Features is using to accomplish the same goal. So you don't need to use Features to do things the same way that Features does really well. You could write that code yourself. But all the other things, right? Let's talk about those. There are things that Drupal expects to be in the database. And this is the biggest problem with trying to deal with a Drupal 7 site for the long term is that you've got configuration in the database, it's a problem, it's been solved in Drupal 8 so we talked about. But Drupal expects those things to be in the database. And so when Features tries to pull them out and put them in code, there's this expensive process of when you're deploying those changes, trying to put them back where they should be so they're gonna be where they are when Drupal's checking to be where they are and that's where things get really messy is when you're trying to do this handoff between what Drupal's expecting and what developers want. And these are the things that are not super safe to manage in Features. This is where you start getting really frustrated with trying to get them to revert. So my crazy idea is like, well, why don't we just leave the things that don't work well where they do work well and try and figure out a way to work around that. So there's two ways that you can manage configuration in the database on Drupal 7. And one of them is to do things manually. And believe it or not, this is how we used to do things in Drupal before we had Features. If it's safe, that's a great plan. If it's not safe, you can write an update hook. And this is also the way we used to manage these things before I see people checking out. Before we had Features, we had to do it this way. So doing it manually, this is not a glorious solution, but it does work, right? If there's a simple, repeatable process that you can do on multiple environments and you can document that, you can redo it. It really depends on how complicated this thing is. If it's a process of creating a content type and adding 18 fields, that might not be something you wanna do manually. Something that's gonna be super time consuming you don't wanna do over and over again. But for a simple task, changing a view, there is kind of a balance there of how worth it is it gonna be for me to do this manually versus how worth it is gonna be to me to try and find a way to do it, not manually. And this is something that I do all the time, which is not for the faint of heart, but if you have a multi-environment setup, which even on my simplest sites, I have at least two copies of the site, sometimes three or four. Depending on what you're doing, creating a content type, you don't wanna do that four times. You might wanna go directly to the live site and say, hey, this group now wants to have a new video type, I'm just gonna create the video type on the live site, I'm gonna add all the fields and if there's something like 15 fields on it, publishers and dates and whatever, you can add all those things, you can export that live site into any other environments and that can be a much faster process than trying to figure out how to get that content type into a feature and deploy it. So rather than moving upstream, which is how developers usually do things, starting in live and moving it downstream is a much faster way to do it, but again, not for the faint of heart because you're dealing directly on the live site. So the other way is writing an update hook and this is something that you do need to know what you're changing, right? Like there's a lot of danger in like, oh, well I'm just gonna change this field display setting and the field display settings are super messy Drupal 7, they're serialized into a single variable and shoved into the variables table, but if you know exactly what that variable is supposed to look like when it's done, you can get an update hook that will take it from the way it is currently modified into what you want it to be and put it back in. So it's something that used to be the only tool we had to kind of make changes to the database. I would recommend in some instances that is an easier route than trying to figure out how to get it and manage it in a feature. So I know what you guys are thinking, I just said custom code twice and a lot of people are like, why would I write custom code when there's a features module that'll do this for me? And for me, I find it faster and easier and less crazy making to deal with the custom code than it is to deal with the feature. So just really quick, the things that we're trying to achieve here. Not harming site performance, so keeping features off the site will make your site faster. Saving developer time, so again this is a balance of like if writing that custom code is gonna be hard and painful and scary for people, that might be a thing that will say it's gonna be faster for this project to do features than it would be to do custom code. But I have found a significant savings in developer time to deal with all of my custom code instead of features. So that's a trade-off and it would probably be per project. And then the last one, keeping yourself sane. So if you like working with features and you think working with features can keep you more sane than trying to figure out how to write an update hook, do features by all means. This is not a solution for everyone. Okay, so I have been able to achieve all of these, solutions to all of these problems by doing it this way. This is my choice. I'm sharing it with you just for fun, not because I think it's the only way to do things. But it's something that I've showed people and several people have expressed interest in learning about it, so I figured it was worth sharing with you guys too. So what's gonna be in this code? I've got a stub module for each type of configuration that can be exported. So that means I've got a module for views and a module for panels and a module for feeds and a module for whatever it is I have on the site that needs to have exports and keep them separate. And this is something I actually learned from using features. When I originally bundled all of my features into individual sets of functionality, like the way I thought it was supposed to be done, I found myself wasting a lot of time looking for like, where is the view that manages the photo gallery? Is it in the one for photos or is it in the one for galleries or is it, I don't know. There's so many different pieces of a Drupal site that can sometimes be one piece or another. It's kind of up to whoever created the feature to decide where to put it. For me, I started grouping them rather than by feature. I started grouping them by type of thing. So then I had a feature for views and a feature for each content type and it was only the database structure and then I had a feature for panels. And so when I started creating this custom solution, I did it the same way where I now have a custom module for views and a custom module for panels. Which just makes it really easy for developers to know exactly where to find the file they're changing. And then almost every one of my projects also has a custom module that's specifically for update hooks. So that way if I need to make a change to the database, I know exactly where that file is gonna live and it's in that separate module for updating code. So I wanna show you some code examples. So this is my module for views. Info file is just really simple. It just has a dependency on views. Depending on what your views need to do, you can add additional dependencies. So for example, this site is using the Flexlider module. So I have a dependency around Flexlider views and I'll show you Flexliders have presets that are also exportable and I'll show you those in a minute in the code as well. So the module itself is really simple. There's a couple of views hooks that you need to implement just to make sure you're running the right version of views. This could be version three or whatever it is your site needs. If you have plugins that you need to specify where those plugins are or what version of those might be needed. But the code that actually handles the export is super simple. It's like, what is this? Less than 10 lines of code, eight lines of code? 12 lines of code. It's just a loop that says, hey, go look in this folder, find a file with a particular extension. If there's a view in that file, load it. So this is how all of your views exports get managed in nice clean individual files. And then I did the same thing again. Views module has a alter hook. So views provides a bunch of custom views. A lot of contrib modules provide default views. You wanna be able to make changes to those default views. You can do it in exactly the same way. So this just says, hey, go look in this folder called views. So I'm not sure if you can see this on the screen because it's really tiny. But in my views module, I have a views folder and inside that views folder there is an ink file for every single view. And this is just what happens when you go to the views UI and you click export. It'll just give you a bunch of PHP. You just copy the PHP and you throw it into this ink file and you save. And I find having one view per file is super easy to manage because then if you have multiple developers and one does a poll, they can see which view has been pulled in. And they can go to their interface and views and they can make sure that it's in a reverted state so that they don't have any changes that are gonna be competing with the changes that were in that file. So it makes it really easy to manage and get. It makes it really easy for developers to know where they are and all of this stuff is right there. So I mentioned before that the views in this particular example are using Flexliders. Flexliders also are using the Ctools export UI which is what, or Ctools export API which is what views uses to manage this like view export thing. A lot of can drip modules in all of Drupal do this. And every single one that has support you can manage in this exact same way. So in my views module, I also have a hook for Flexlider defaults. And the only difference between the Flexliders and the views in this code is the folder I chose to put the ink files in. And that's just so that when I'm in this Flexlider UI, I can say export a Flexlider. I know exactly where to find that. I've got a sliders folder inside my views module and I've got a couple of Flexlider preset exports here too. So again, depending on how you wanna manage things, you might wanna have like a separate custom module for sliders that's different from your ones for views. But in my case, I never use a Flexlider preset that's not part of a view. So it made more sense to keep those in the same module cause they always went together. But again, it will depend on your site and your developer's workflow, how they wanna organize things. So that's views. Panels, does anybody use panels on their Drupal seven sites anymore? A couple, that's pretty good. Okay, so panels are a little bit more complicated than views. But the model is exactly the same. So info file uses panels and page manager. I also have an additional dependency here on a module that provides a bunch of layouts cause my panel pages use layouts from that module. But again, it will depend on your particular setup. Make sure you have your dependencies clean. That's the only point here. And then the panels module is the same thing where it has version requirements. This is just the normal hook you use for panels. And then there's another, a couple of looping hooks. There's one of them here that handles handlers. So panels handlers are the things that override existing paths. It's something like nodes and taxonomy terms. Anything that's already existing and you wanna alter the way that menu callback works. So I have a folder for handlers. And then I have another one for pages. So pages are things in panels that you make yourself. So like the homepage, something that doesn't already have a path. And those go in a separate directory too. And the reason I chose to put those things in separate directories is because the user interface is different. So where you are in Drupal when you choose to export is different when you're on a handler page. You're not exporting the whole thing. You're only exporting the little bit. So you have like no type article, no type page or whatever those specific bits are at smaller amounts. And so I found the people I was working with had a harder time figuring out which UI you exported from. Cause in panels you can do both. So I separated them on the code side. So it's like okay, if you know, you're gonna look in your handlers folder and you're gonna find one for a specific, basic page content type. It'll tell you okay, well if this is a handler you're not gonna be exporting the whole page. You're only gonna expect exporting one for that type. So it's just something that I developed over time. Again if you guys wanna put them all together that's really up to you. And then same thing here. I have text on it, my term example. But whatever you do in panels you can export cleanly here. So handlers pages and then if you're using the mini panels module those also export in another place in Drupal UI. So I created another folder in my panels module. So I've got a folder for handlers, a folder for minis and a folder for pages. And so depending on what you're managing and where you're exporting it you can organize it cleanly in your code to match. And again that's to make it really easy to manage in Git. So let's talk about some of the examples. All of the views and panel stuff we just looked at even including flexliders and mini panels. Those all use the CtoolsExportables API. But there are things in Drupal Core that do not use CtoolsExportables. And those are things you sometimes also wanna be able to manage in code. So three of the most common examples I do on almost every site are view modes, image styles and date formats. And so in my code I have three separate modules for managing those. Image styles are the most common so I'll do that first. Again very simple info file. The module has two hooks. So these are not CtoolsExportable API hooks. These are just Drupal Core hooks. But there's one that is default image styles. So if you wanna add your own default styles like things that don't exist already in Drupal you can just export them with whatever image effects you wanna have. If you're using contrib modules that add additional image effects you'll again wanna make sure the dependency is there in the module but you can still export them in the same way and get them into this file. And if you want to modify an existing image style there's also an image styles alter hook where you can take something like there's three image styles that come by default in Drupal 7, small, medium, and large or thumbnail, medium, and large. You can change those names, you can change the styles, you can change the sizes in the same way just by using this alter hook. So this is usually how I do this. I find that usually what happens with image styles is I end up creating them in the beginning and they don't change very often. So this particular module is adapted from a site that was built once in these image styles have never been changed. But I do find that if you have a site that is changing a lot or adding a lot you can also put a little loop in here and put each one of these into its own PHP Ink file too if that's easier to manage that by. So similarly here we have date formats. Same thing, there's a hook and core that lets you provide default ones, there's a hook and core that lets you override them so if you want to add your own date formats you can. All of this, well I'll get there in a minute. So here I've got an example of like I'm adding four new date formats and I'm defining types that work or what the actual string code is for each of those date formats. I'm changing names on existing ones just to make the interface less crazy. And then I have, the other one is view modes. Here's an example of the view modes module. It doesn't have any dependencies. I just added an extra one here for something called widget. And then I have a fieldable panel pane as the module I really like. Here it's got an extra view mode for those so you can control how those look. So if you want extra view modes on your nodes or whatever you can add them in the same way. Okay, I know I was gonna have time. Really quick. We talked about that custom code is not that scary if you have an example to work from. And this is open source. So I've open sourced all my code. You can find it at github.com slash gen left and just generate. So if you wanna refer to this in the future you totally can. And this works for me. Might not work for you. Feel free to give it a try. And we're out of time. If you do have questions feel free to meet me in the hall. I know there's another talk that starts in like three minutes. So that's it. Thanks guys.