 So, welcome. I'm William. I work in Drupal and Aquia's Drupal Accelerations, not the other way around. And I submitted this talk pretty much immediately after Drupal called Pittsburgh, and I didn't know how much we would get done in the meantime. So it's almost the same description. And I was hoping it would be more concrete, and I'm happy to say that thanks to Boris at the front here as well, we've gotten way further. And so I'm going to talk to you for about five minutes, and then I'm going to do two live demos of about five minutes each. And my goal is basically to have you walk out of here, hopefully excited, definitely knowing, definitely having a feeling that you know how to get started with using conflict validation. It's not, like, not everything you're going to see is already available in Drupal 10.1, but many things are just the four bits and pieces in the demo are not going to be available just yet. So yes, you have to wait for Drupal 10.2, but everything else is. So let's dive right in. I want to give you a little bit of context for why we're even doing all of this. So a brief history of the conflict in Drupal. In 2004, in Drupal 7, we had a variable table with key value pairs. The values are, like, massive, massive arrays of doom that then are serialized. So, like, it's completely undecipherable. In 2012, during the Drupal 8 development cycle, we turned them into YAML files. And now YAML is diffable. It's diffable. So at least it's a diffable blob. In another year's time, we added schema support. And so that made things introspectable. We could kind of make sense, like, what is the actual stuff in there instead of, like, random blobs? So that was better. And we made things translatable thanks to that as well. But guess what? By moving to YAML files and having to read dozens or hundreds of YAML files on each page request, yeah, that's going to be slow, so we had to move it back into the database. But it's just, like, reading YAML files and storing the database once. So performance was fine again. Drupal 8 shipped. And then after Drupal 8 shipped, we added validation constraints. And so, in principle, we made things validatable. And so the trend, and that's kind of what the picture was that I was trying to paint, is that we're adding more structure over time. We removed the need for the equivalent of the Drupal 7 variable module, also the internationalization module, and also C tools exportables. Razor fans, if you are still familiar with those names? Oh, wow, yeah, okay. That's a lot. That's not what I expected, but cool. Welcome. So we added validation constraints to config. Cool. But what is actually then validatable? Well, this is the only one that was added back then, and was also the only one in Drupal Core up until December of last year, December 2022. And even though it was the only one, and yes, we have to ask coverage for it, unfortunately it was not being used anywhere. So it was just used in a test, like we knew it worked, but it wasn't used anywhere in Drupal at all. So, fine, that's where we were at. But why even do all of this work of config validation? Why would we care? Why should we care? Well, there's a bunch of things that are just not possible without config validation. I worked on JSON API and API first things long ago. And one of the frustrating things was that it wasn't possible to make it possible to write or change configuration via REST, via GraphQL, via JSON API. And that actually was a direct blocker to admin UI improvements, and specifically the JS admin UI initiative, which show of hands who remembers that still, yeah, fewer, but many do. Unfortunately, like all of the validation logic was tied into, was written into the existing PHP form. So of course, a admin JS initiative, they can't magically make the PHP code work in the JavaScript code. So we really need that configuration to be validatable in a more structured approach. Another thing is advanced configuration management. Show of hands of who's using config splits. Whoa, okay, pretty much everyone. Cool. So I don't know how many of you, some of you have run into problems with re-merging split configuration. I see at least one person nodding over there. I see somebody nodding strongly. No, okay, a little bit all over the place. Some of you have run into it. Some of you haven't. Those of you who haven't have been lucky. Because this is not a fault of the config filter module at all, nor of the config split module. It's just a limitation of what the information is that is available to that module to do the right thing. So that's another thing, that config validation aims to make better and actually aims to make a thing of the past that you ever run into those problems. The recipes initiative is all about configuration. So you can imagine that if you were to apply a recipe, you kind of want to make sure that it actually is going to work. It's going to result in a valid whole. And that's what config validation would also make possible. So recipes would become richer in experience, trial experience could be possible in theory and so on. Automatic updates. You can imagine that quite a bit of update hooks are about updating configuration. And what if you get something suddenly wrong? Then your configuration has been updated incorrectly and good luck digging through that to figure out when it went wrong, in which update hook and so on. But if we had config validation for the things that we're updating, then you would know right away when writing the update hook that it's not going to work correctly. And then finally, ambitious side builder experiences like the recent field UI improvements that unified all of those different screens into a single screen. There was so much work and so much pain there. And quite a bit of that pain was due to the absence of config validation because again, all of that validation logic was in the forums. So I don't know, but I have the impression that all of you have at least one of these things because pretty much all of you raised your hand at config split and config filter. So these are things that it aims to improve. So okay, the concepts that come into play and I'm not going to go into detail in any of these. That's not the point of this session. The session is about making you aware of these concepts and how they connect with each other so that when you encounter them, it's going to be hopefully really easy and fast to dig through them and observe them in detail in your concrete use case. So as I mentioned, TypeUUID is the one thing that existed in Drupal Core. Now, since you all raised your hand, I'm sure that you've all seen a drush config export results. So you've seen plenty of UIDs in your life. Those UIDs actually are coming from or are defined by the schema by this TypeUUID thing. Now, what does that actually mean? Like what does that string UUID after type colon actually mean? Well, it refers to a config type and a config type is a top level key in one of the schema YAML files in a module or in Drupal Core. But that's just the type that's a name. We also need to be able to store it somehow when we're saying that a UUID is going to be stored as a string. We don't want to label, but we don't want to just store any string as a UUID. We want to make sure it's the correct shape of string so that it actually is a UUID. So we need to specify constraints. And that's where things become more interesting. So now you see uppercase U, lowercase U, ID. So that's a different string. And that's referring to an ID of a constraint plugin. So this kind of thing. A plugin annotation. So you can see that this UUID matches that exactly. So we went from a schema YAML file. We are defining types. And now we're saying that the types need to be validated by constraints. The constraints are defined by a plugin ID. That's this thing. So what does a validation constraint actually look like? It looks sort of like this. You have a class name of arbitrary naming choice that you would like to do. And it must always extend the symphony abstract base class called constraint. But this is just defining, okay, what is this thing that is about to be validated? There is no real logic usually in a constrained class. The actual logic lives in the validator. And the validator is following a very similar pattern. So you use the exact same name as you had before and just append the suffix validator. Then the two are connected. Symphony knows that these things belong together. And it extends, again, an abstract base class called constraint validator. And now the actual logic of validation begins. You implement a validate method. It receives the value to be validated. For example, something that kind of looks like a UID, but maybe isn't, or maybe it is. So you receive the value. You receive the constraints. Constraints can have options as well. Meaning you can configure them sort of in a way. For example, you can imagine if you're saying that integers have to be in the range of 0 to 20, then there would be a min option and a max option 0 and 20. That sort of thing. So you feed in that information into the validator. And then the validation logic begins. But before we really begin validating, we first make sure that the things we received make sense at a very basic level. So this is a UID constraint validator. We make sure that we actually received a UID constraint. Otherwise we bail out, we say, nope, can't deal with this. And we verified that the value is at least of the very basic shape that we need, meaning string. If it's not a string, we don't know what to do with it. That's it. This is all of the moving pieces. So congrats. You know all of the concepts that are important in config schemas, config validation, and so on. So quick recap going the other way around. There's a constraint validator. It's connected to a constraint, which has a plugin ID. The plugin ID is mentioned in the constraints key inside of a type in a config schema. So that string again. And then there is a name for the type. That's a top level key in a schema YAML file. And that is something that we can use everywhere in our config schemas. So those are the moving parts. Time for live demo. So I'm going to show you, and this is the one thing I forgot to check ahead of time. Is this legible or does it need to be bigger? Is it good? Okay, cool. So I'm going to do a fresh install of Drupal 11, but it's the same as Drupal 10 too. So you can see I'm just doing a fresh install with very safe passwords and so on, as well as installing a config inspector module. That's it. Nothing else is going on. So back in Pittsburgh, this is what config inspector module was able to give you. You could analyze a particular thing, for example here, media settings, and you could ask it to list more detail and give constraints. But there's not that much there, and there's a lot of things that are clearly not yet validatable. So if you would do the same thing today, it would be a much more typical experience to have something like this, where at least the common patterns, like core default config hash, is fully validatable. That's part of the progress we made, and you get a lot more precision and a lot more help in order to make your things validatable. So you can see here these yellow warning signs with an attitude, and it tells you where exactly to add the validation constraint. In this case, it's here, but it could also be somewhere else if you're reusing some other time. So that's enough about that. Let's actually just dive right in, and let's take a look. So I know that my drush is installed in vendor bin, so I'm just prefixing it with that. And there we go. So I've just analyzed all of Drupal Core after a fresh install of the standard install profile. If you were in the keynote, you saw that I said that 6% was fully validatable, and that's just me doing this command effectively, wrapping for which ones reach 100%, dividing by the total, and that's it. So this is interesting, but it doesn't tell me that much. This is just for kind of an overview, and eventually, hopefully, everything is going to show at 100%, of course. So let's figure out what things remain to do, and I need to resize slightly to make this fit. I hope this is still legible. Okay, cool. So that was too much. Let's limit it to 5 to 2, so that hopefully it's going to fit on screen. I'll skip to 3. That's kind of okay. Okay, so we analyze it, and we find low-hanging fruit. If you were in the keynote, you saw this too, but now you can see it live, and how fast it is, how easy it is to do this kind of thing. So we can see that no-type article, no-type page are both at 93%, no coincidence, because they're both like the same thing. They're both no-types. High-hanging fruit, unexpectedly, no, not really. Very expectedly. Views, field, fields. A views thing, which is super complex, super advanced, is in a lot of places. So that would really help. If we could make that one validatable, we would get 8% of the total remaining things that we need to make validable still. If we fix that one thing, if we make that validatable, we get like a massive increase in overall validability. But let's look at one concrete thing. This no-type article seems kind of interesting, because it's almost there. So what would it take to get it to 100%? Okay, that's a little bit cramped here, so I'm gonna select a few columns. I'm gonna look at key, this data column, and constraints. So I'm gonna say, and this is not something specific to this command. It's something that's built into Drush and anything that provides stable. So key, data, constraints. So then I'm gonna get all those three columns. That's better. Okay, so we can see there's a lot of double green check marks, meaning it's fully validatable and valid. The question mark is at the top, is not everything is validatable yet? We know that, because it was at 93%. And there is one thing here with it to do. And it's telling me to add validation constraints to node.type.star. Okay, one thing I didn't say before is that every config type, it always starts with the name of the module. So node.type.star, doesn't matter how many dot whatever there are, it's always the first thing. So node, I know I need to look at node schema. Okay, so let's open node schema. I'm gonna zoom in. And let's see what was the name of the thing. Preview mode, okay. I'm gonna search for preview mode. Preview mode, type integer, okay. Interesting. Well, let's take a step back and go look at the UI. And I think I need to turn on my tabs. Even though I open the new tab, it's refusing to show it. All right, so logging in with my super safe password and going to reports, configuration inspector. Yes, okay. So I'm not gonna show you this UI. It's existed for a long time. It's showing a similar view but then through the UI instead of through the command line interface. It's the same 93%. Let's look at the data in there. So we should know, we know already actually that preview mode is not validatable. That's the case. The current value is one. But I have no idea what that one means. Like is it one? Could it be a million? Could it be minus seven? I don't know. But I'm just gonna wing it. And I'm gonna add a constraint. I'm gonna say a constraint. I'm gonna say the choice constraint. This is one that's gonna be super useful. You're gonna use it all the time. So this means like of the choices that I'm providing, it must be one of them. Anything else is not allowed. So I'm gonna pick our favorite number, 42. And I'm gonna rerun this. Okay, cool. There it is. Choice 42. It's fully validatable. It's claiming to be validatable. It's telling me one error. Well, that makes sense. I just restricted it to 42. I already know that it was actually one. So of course I wanna make sure that it works here too. Yes, it's validatable and it's complaining. Okay. So I need to actually properly understand this before I can add validation constraints, which makes sense. You need to understand what the data is that you're storing, what the domain of value is. So let's go look at no type. I don't know what this is about, this preview mode thing. So opening no type, starting at the top, searching for preview mode. Yeah, that's not gonna help me. Ah, var integer preview mode Drupal optional. I don't know what 2014, that's a long time ago. Okay, let's take a look, zoom in. Okay, so this is the one. Probably the one, so probably it's a default value. I see Drupal require Drupal. These seem to belong together. That's something completely different. I'm gonna, like I should investigate more closely, but for now we're gonna go with zero one and two. Zero, one, two. And hopefully that means that now, yep, it's valid. And yep, it's fully valid. So if I refresh this now and go to no type article, that's it, it works. So that was number one. This you can do in Drupal 10.1. This you can do even in older versions if you will. That config inspector module works for all of those versions. So you can start using this today in your deployment workflows to validate things on a deeper level if you like. So, second live demo. It's cool that we can do this in the command line and we can see it in the config inspector, but it wouldn't be better to actually bring it into the UI and into the forms. So this is the other thing I mentioned in the keynote. There are new optional methods in config form base in Drupal 10.2, so you're not gonna find them today. And the install that you're working on, unless you're working on Drupal 10.2, it might be brave. So, let's go look at the change records. And if I scroll down, it's telling me here, what does it look like? It's telling me to overwrite this one method. Okay, let's go take a look. I can close this art. So config form base. Let's find that method. Okay, what is in there? That's weird. It's telling me this allows submit form and validate form to know that this config form is not yet using constraint base validation. Huh. Okay, so this is a thing to retain backwards compatibility, but something must be using this in Drupal Core. So let's, okay, there is only one, because PHP storm didn't give me a choice. So there's this one example. Okay, let's see how we can use it. Let's go back to our freshly installed site. I'll go to configuration. This is the first thing. Let's go there. And, okay, this string I'm going to do something to, I think. We're in France. Maybe we can restrict this to something more French. So let's find out, okay, I'm going to take this description because that's probably going to be unique. I'm going to search the entire code base. It's two matches. Interesting. This is in migrate Drupal account settings form. That's got to be it. So zoom in again. Yeah, that's the thing. The name, the sub-match. Yeah, okay, so I'm in the right place. So I'm retrieving the anonymous key or property path to be more precise from this dollar config object, and that's going to be the default values. That must be what's currently stored. So if I try to find where dollar config is, oh, there it is. And it's apparently user.settings. Okay, so user.settings, if I were to open that, it should tell me that, yeah, okay, that is the same anonymous. So we know already user.something means that it's the user module, so we can open user schema. And we can search for this key there, anonymous. Oh, yeah, it's right at the top, makes sense. It's already got a type that is a little bit more specific than it was in the past, but I want to make it more specific. I want to constrain it more. So I'm going to add a constraint again. And I'm going to say, and I want to do the same thing with looking at it in the command line again, but of course I want to look at user.settings now. And if I look at that, yeah, cool. It is there, and it inherits it because that's what this error thing means. It inherits it primitive side because it's a string. It inherited this kind of crazy looking regex because it's a label and we don't want control characters in labels. That sounds good. And it's a required label, so that's why there's also the not blank constraint. So all of those are inherited. They're still there. I'm just adding to it in this particular case. So you don't have to define new types all the time. You can reuse and be more specific. Okay, so it's in there. I'm just going to go and try to save this. Maybe it works? Yeah, no, it doesn't. Because I haven't done that other thing yet. The copying of that, yeah, this thing, this copy form values to config. So it's a pattern I have to apply, so I might as well copy paste it. So I'm going to do that. Okay, I know it's on update settings here. It's user settings, so changing that. Yeah, these things don't exist. These are for update settings, so I'm going to remove that. But hey, look at that. The submit form is dealing with something very similar. And yeah, this is something I can just copy paste. This is not a coincidence. This is something that's going to be true all the time. So I can move this around. And actually in the future, that means I will be able to just delete this. And most forms will not just be able to delete all of this. But for sake of demo, I just want to show you that this is all it takes to connect the two things. So assuming it's now connected, I should be able to just hit save and it should work. Let's see. Cool, that worked. But I was going to show you that you can easily tweak this validation message, but I'm over time, so I'm not going to do that. But you can see that it didn't take me much time at all to get this working in the UI. So a few steps, a few minutes. It's a repeatable pattern that you can apply, and you're going to be able to benefit from this. If you have a contract module that you maintain, you can also optionally implement the config events, save events subscriber to always make this validation run so that even when it's being imported through Drush, for example, that it's still going to be validated, that you can be very confident it's correct, it's going to be even better for you. Thank you to these fine people to have helped us get this far. And if you're interested, follow the meta and come find us tomorrow at Contribution Day. Thank you.