About this video
What You'll Learn
- How to apply BDD and example mapping to turn uncertain product behavior into concrete, testable rules.
- How to identify and document Git repo cloning and reconciliation edge cases before writing implementation details.
- How to transform example maps into Gherkin syntax and connect scenarios with a practical Rust implementation plan.
Ciaran McNulty walks David through Example Mapping and BDD applied to a real Rust GitOps library, capturing rules and examples for cloning and reconciling Git repositories before translating them into Gherkin scenarios.
Jump to a chapter
- 0:00 Holding screen
- 0:40 Introductions
- 1:24 Introduction: BDD and Example Mapping
- 2:20 What is Behaviour Driven Development?
- 2:47 Defining Behavior Driven Development (BDD)
- 5:30 Applying BDD at Different Levels
- 10:27 Project Introduction: Get Sync and GitOps
- 10:30 What am I building? (GitOps Rust library)
- 17:17 Introducing Example Mapping: Components and Purpose
- 17:20 What is Example Mapping?
- 24:15 Mapping our domain
- 25:08 Example Mapping: Initial Repository Clone Feature
- 30:06 Discussion: Depth of Detail and Audience
- 40:30 Discussion: Capturing Rules and Evolving Design
- 47:50 Identifying the Reconciliation Feature
- 50:38 Example Mapping: Repository Reconciliation Rules
- 1:08:46 Transition to Gherkin Syntax
- 1:12:57 Writing Gherkin for the Initial Clone Feature
- 1:14:32 Gherkin Syntax: Rules, Examples, and Documentation
- 1:25:21 Using Gherkin for Implementation and Testing Strategy
- 1:38:28 Summary and Value of the Example Mapping Process
- 1:42:50 Conclusion and Future Steps
Full transcript
Generated from the English captions. Timestamps jump the player to that moment.
Read the full transcript
1:24 Introduction: BDD and Example Mapping
1:24 Hello, and welcome to today's episode of Rawkode live. I am Rawkode, your host. And today, we're gonna be taking a look at behavior driven development, example mapping, and a little bit of Gherkin potentially. To do that today, I have a friend of mine who is my go to in all of these topics, Kieran McNulty. Hey, mate. How are you? Very kind. I'm good. Thanks, though. Yeah. Good. Good. Good. Yeah. You are my go to. I think I definitely send you far too many messages going to help me, please. I have no idea what I'm doing. So I'm glad that we
1:58 can do this in a way that we can share some of that knowledge with other people. And hopefully, using a project, but it's something I'm actually really working on as we can show them how this applies to, like, a real domain and and and hopefully break it down a little bit. Yeah. Great. I'm sure I send you an equal amount of Docker and Linux questions. Yeah. Maybe our friendship's a little bit too transactional. We'll see. So, like, we should probably start then, I guess. I mean, am I right in saying that we're gonna use example mapping to understand what
2:20 What is Behaviour Driven Development?
2:32 I'm trying to build with a little bit. Does that immediately go to Gurkin and scenarios? How did all these bets go together? And in fact, back up. What's behavior driven development? Have you got like a a thirty second pitch? Sure. BDD is one of the one of the characteristic things about the BDD community is that isn't really strictly defined. A bit like kind of a bit like DDD in that way, in that there's lots of different ways of doing things. What they have in common is a a kind of approach a set of practices to be used in
2:47 Defining Behavior Driven Development (BDD)
3:08 agile type environments that encourage fast feedback loops. And BDD emphasize definition is good. BDD emphasizes talking about behavior using examples, and that leads to many things. It comes from the TDD tradition where you write a test first. That that that's the kind of genesis of BDD. Dan North was trying to teach people to do this. And there's a kind of stumbling block when you tell people to write tests first, isn't there? Because it doesn't make sense. Right? When you go to school, you have the test at the end. You learn the you do the thing, and
3:48 then you then you get tested. So Dan, who I think was into things like NLP and that kind of stuff, decided to to to not use the word test and instead talk about describe the behavior first and then implement it. So you in BDD terms, you sort of think about those things as specifications or, you know, descriptions of what the system's going to do, and then you implement it. And then you somehow automate a test against that description. So it's it's the same TDD cycle, but you're sort of thinking about it a bit different. And that was, you know,
4:27 twenty years ago. Yeah. No. Twelve years ago. Yeah. I I I'm not sure about like this. You want me to know what I'm writing before I write it? I mean, that sounds blasphemous. You need to think about the thing. Oh, no. What I mean is like, maybe maybe it's just me and being an absolute caboy of a developer. But generally, I just hack at the code until I think it does what I wanted to do and then trying to look back at it. And I I know that's a wrong approach. And I've tried to get into TDD many many times.
4:58 Right. Well, I appreciate the design improvements my code has when I do TDD. There's there's still a, like, a wall you have to get over to really Yeah. Ingrain that approach on you. And I I don't feel that I'm there yet, but I do I am I am getting better. No. That's that's very very true. And there's certainly a good argument that a TDD approach isn't a good fit for all problems. If I've got absolutely no idea how I'm gonna do something, it's hard for me to write tests. So another element of diagnostic BDD definition is
5:30 Applying BDD at Different Levels
5:31 that it's sort of multilevel. I can't remember the exact phrasing, but it it can be applied at different levels. So it it may be just to give an example, it might be I'm gonna write a script that's gonna recognize images in images of faces in photos. And imagine I'm the first person who's ever thought of that. You can't just rent time to do that on some cloud service. What test can I write? If I don't know how I'm gonna do that yet, if it's a sort of r and d thing, I can't write a test
6:03 easily at a low level. I can write a test at a higher level, which is, you know, if we get all these photos, you'll know who's in them. But a lot of the work I'm doing underneath that level won't be driven by TDD. Right. Just because I'm just hacking stuff together. So I find that a lot in those very sort of unknown areas. I I don't do TDD particularly. I'll hack something together. If that's good enough, I won't worry about things. But if that's something quite important, once I've hacked it together and I've kind of got
6:41 a mental model of it, I maybe will write a clean version that I do do TDD. Because I kind of know what the bits are gonna be and how they'll fit together. Oh, yeah. Definitely. So something that BDD has has grown to to talk about a lot is acceptance testing. So when I'm talking to someone at business level, or maybe I and the team, we're having to talk about what we're gonna build this sprint, you know, build next. You can apply a BGD approach where you think what what what's it gonna look like when it's finished?
7:17 That kind of feature level acceptance test or acceptance criteria setting. That's a nice level to talk about because you you can have that conversation at a level that the business is actually involved in. So it helps bridge some of that communication gap. You know, before we build it, can we talk about what we're gonna build? Isn't that controversial a conversation topic? And you can do it in a way where the technical people will then figure out the technical implementations after. So you can have a sort of, like, business level conversation, and that's that's tends to be where
7:55 people use BDD practices like the ones we're gonna talk about today. Yeah. And in fact, when we were talking about potentially doing this session, one of the things we said was, so the thing I'm trying to build, I want to be open source and I wanna be able to tell people what it does and then a a way that kinda reveals the details of how it works. But, you know, I didn't really know that myself either and what we've decided was that this process would be a way for me to kind of submit my own knowledge, what I wanted
8:21 to do and how it works, and then shoot up with other people and, you know, we kinda get benefits without even necessarily gonna straighten to the testing aspect of it. Just that language and the examples tell people, hey, this is what it does. This is how it works. And, you know, that might affect your use case. Yeah. And I, you know, you know, I work with different teams trying to do this. And I think the situation you're in is something similar to a lot of teams I work with where they've started with the testing. Right? Yeah. And then but what what maybe
8:51 some of people in the audience don't know how Gherkin looks, but one of one of the sets of tools that people use for BDD is Cucumber. Others are available. And one of the things you do is you write down the steps of each test in English. Other languages are available. And why bother? It's it's because that's then gonna get used for something. But if you're starting with the test and then you're just then you're going back and sort of saying, well, how will I you think of the test and you say, how am gonna write that in English?
9:26 It it it's kind of backwards, and it's harder. It's much harder to do. So the reason I suggested example mapping, which I often do with these themes, is, like, let's do it top down. Let's start with your understanding of what it's gonna do and then drive out examples. Yep. And then some some of them will be acceptance tests. Because otherwise, you're sort of imagining what the conversation we had before would have sounded like if we'd had it. Alright. So should we well, first, we'll do a couple of the comments. So Mark Taylor, looking forward to this. Yep. Me
10:04 too. Glad you are. And common friend of ours, James. Hey, man. How's it going? Also, because I've been on PTO time off for a week, I've forgotten how to stream and actually do my job. So what I'll also say is I wanna say thank you to Equinix Medal. I'll put a code on the bottom of the screen there. If you wanna check out Equinix Medal, there's a $50 coupon. Tell your bits. Right. So the domain that we're working on today, let me set a little bit of background on this. I do a lot of get ups. And if
10:30 What am I building? (GitOps Rust library)
10:35 you're not familiar with the term get ups, let me try and give you the thirty second version of that too. Get ops is a method of continuous delivery where we declare and that's a clarity of fashion what we want to be running and our production environment through manifest. Like, I do a lot of Kubernetes. So we generally use YAML manifest. Controller lives inside of the Kubernetes cluster and continually pulls that get repository and applies it into the cluster so that no matter if manual changes happen to the cluster, they get blown away very, very quickly. That's the very
11:05 quick version of what get ups is trying to do. Now I started applying this to more models now that aren't just Kubernetes. And one of the things that I felt internally in our team at Equinix Medal is, like, a static site generator that pulls from YAML parses that renders a site. I want that to be continuous as well. Like, whenever someone pushes a change to the YAML documents inside the get repository, I actually wanted to serve that over an API GraphQL as a database. Yeah. I can see that weird look in your face, but it's actually a really nice
11:39 approach. In fact, it's strange that James is on this because he's written a YAML database before. And this is not actually too different except get as a database and I'm just continually updating my models and memory and delivering over an API. But it means I can have a almost static or purely front end generated site that knows all those Jamo documents via the API. The why, I can definitely cover more later, maybe as examples future episodes. But for today, what I wanted to well, sorry. So what I started doing a month ago was extracting that get logic to
12:09 a library so other people can build and roster on get ops like operations. So the library we're using today is here. I've not touched it in a month. A month is when I reach out to you, go and help. I have no idea what I'm doing when it comes to BDD. So I decided to keep this mostly mostly for today's episode. So it's called get synced. It's on getlab.com/Rawkode/getsynced. Right now, it's very very trivial and it really just clones our repository. And that's where that starts to get a little bit complicated, is why I reached out
12:47 to you. Because there are a whole bunch of rules and semantics that I really need to declare first. And I wanted to do that viewing of with feature files so that I don't make a mess of it. Because what happens if I run this program, it codes the repository, it runs in a loop, pulling all the changes, and someone makes a manual changes. Do I reset the repository and then fetch again? Do I throw an error? What if there's a force rate on the remote and I can no longer pull changes? What's the procedure to rectify? And there's a
13:18 whole bunch of these I mean, we all work with GET. We know it's painful when something unexpected happens. And I need to be able to codify that now and these feature files so that when other people want to use this library, they can go, what happens if someone forcefully writes to the remote and then it's out of sync? Like, what are what are the rules? And Yeah. I want us to use example mapping to try and document that. That make sense? Do you have any tests at the moment? Yes. There are. So in Rust, I have checked that it
13:50 can clone if there's nothing there, which is the simplest test I could get. And then oh, wait. That's the clone repository. Oh, yeah. And this one is that check to see if a clone exists, but we don't I don't have any test to confirm the logic of what happens when it does exist and it tries to clone. So I can check the the check that there's a get repository where I want it to be, and I can check that I can clone if it doesn't. And then that start to write a feature file here and got really really lost. You can see
14:22 my sex laser gherkin before my brain exploded. And it was really just trying to me trying to take that test I'd already written and write in the feature file, which you just told me, yeah, don't do that. So You can. I mean, if it the use case you've got, which is you're hoping people are gonna read it in the future is a good reason to do it. Right. So retrofitting in that way, I'm I'm not a % against it. It's just harder. It's just the knowledge is harder. What what's hard to do is if you don't know who the intended audience
14:52 is, I think something you maybe struggle with is just looking at it. It's hard to know what level of detail to go into. So when you're talking to real people involved in the project, a mixture of business people, test people, dev people, you will you'll talk about the project, especially if you're having a quick conversation. You'll talk about a project at a level of abstraction that the business people care about. What I mean is there'll be a sort of there'll be a depth where they care how it works. Like, it's really important that this thing happens. It's really
15:31 important that this thing happens. And everything under it, they don't care about. And that's really different for different groups of people. Yeah. What happens in, like, dysfunctional waterfall type situations is that you spend ages getting the business people to agree to all the detail and writing it all down, and there's a load of stuff they really don't care about. So some teams, it may be like, I need to be able to do this action somehow, and I don't care where in the system I do it. I don't care what the steps are. Like, I just I just
16:03 need that capability. And if I've got that after you've got the feature, if I've got that, it's a tick. Let's go. Other people may really care. It's in the specific sequence of steps. There may be really important business constraints to to put in there. So what what I'm getting at is that the the real conversation naturally finds that level and capturing that in the scenarios is a nice way of doing it. Yeah. I think that depth is one of the things I've definitely struggled with. I mean, I've tried to adopt BDD multiple times over the years. In fact, I think the first
16:32 time we met was when you were doing BDD training at a company I worked for, going back to what? It's like five, six years now. So Maybe more. Yeah. Maybe more. And, you know, maybe it's just my perception of the BDD community or maybe it's the journey I've taken is just not normal. But I've seen examples where people say, oh, it should be high level because it shouldn't get into the implementation details. And then I've seen examples where they actually do kinda get into the implementation deal. Like, the depth seems to change and now we're back up with another
17:01 what I see is a different version of BDD where it's okay. You shouldn't cover implementation details, but we do have examples which are inputs and outputs. Or I I mean, it's all very blurry for me. It's not something I have a a good understanding of. So let let let's talk a little bit about example mapping. So Cucumber's been around for ages. Cucumber is what that Gherkin syntax comes from. Matt Wynn wrote about example mapping maybe five or six years ago, and it's you know, the so the way Matt talked about it, they're they've been using different workshop formats to
17:20 What is Example Mapping?
17:38 try and come up with these scenarios and, like, make it less about typing stuff into an IDE, more about talking about it first. And, you know, there's still lots of ways of doing it, but example mapping is the one that kind of clicked for him. And he blogged about it, and I started using it. And it's a good starting point. It's a really great starting point of how are we gonna have these conversations in our in our organization, in our team. You can you can tweak the format, you know, for what works for you. Yeah. But
18:08 let we can sort of do a sort of vanilla example mapping and and and see how it goes. If you if you show the mirror, I can sort of explain what the components are. You shouldn't have sent me at all your print, my joke. I was gonna tell you to get your whiteboard out. I do have loads of cookies. You got loads. Of course you do. Of course you've got stickies. Well, because I'm a consultant. I went big on buying loads of stationery and then I've been sitting in my house for the last so this is just a generic whiteboard, but
18:38 the the let's talk about what the primitives are. So you're in a different part of the board to me. I am I'll I'll meet you in the middle. Hang on. Oh, there we are. Right. Here we go. Zoom out a little bit. So we have example, think who's in it? People who know people who can make decisions about business stuff. So like a product owner type, BAs. People are gonna build it because it's important they understand it. And if you're not, you know, if you're not in the conversation and you just have the notes from the conversation afterwards, you're not
19:19 you don't have as much, like, implicit background knowledge. So is this supposed to be a Meagles thing or is this different? Yeah. And the people who are gonna test it. Alright. Okay. And the point is to early on align the expectations of all of those people about what it's gonna do. So it's called a feature. Right? So what's I'll just do a I'll just let's do the format first. You put a sticky on the wall and you say, right. This is the feature we're talking about. Yep. Just to give it some sort of scope. You may sometimes people talk about multiple features
19:56 at the same time. I found this kind of conversation, it's best to just do it in twenty minutes and talk about one thing and do that more often rather than your afternoon of death when we just stay in the meeting room the whole time. So it's nice to do a quick, you know, twenty to thirty minutes. And then you're gonna say a feature is gonna give someone the ability to do something, which is gonna make some sort of change. Right? So they're gonna be rules. So you're gonna try and capture what the rules are. And
20:37 just by giving an example is a good idea. So a rule is something like I've overused this example, but a rule is something like clothing is charged 20% VAT. Okay. So maybe that's not a rule of the system. Maybe it's items get charged at the rate for the category of product they're in to make it more future proof when the VAT rate changes. Right? So the rules are really the things that are gonna actually be implemented in the system somewhere in code. Okay. But in general, each rule is is either gonna be implemented in code,
21:22 specifically in the software, or is gonna be implemented offline through some sort of policy. Okay. So should we try and map my domain to the first feature in the first row or not? Let just talk about the other two things. So the the good thing about features the good thing about rules is that's the thing we really want to understand. What are the rules of the system? What are the program what algorithms are the programs gonna have to implement? But then to make sure we understand them the wrong color. We give examples. And when you're in example mapping, you're not
21:59 gonna write out a big old paragraph, but you're gonna give an example of how that rule would be applied. And you'd think for most rules, you'd need at least one example. They're being implied in real life applied in real life. And we scope we scope the examples to the rule. Some would need more than one example. I'm sure you can think of a rule. You know? You get one one I've used in talks is if you get free shipping more if you buy more than the free shipping threshold, you need an example where the free shipping
22:30 threshold is £50 and I spend £60, so I don't pay any shipping. And I need another example where I go this. So so how do you decide how many examples to give? We're not worried about testing yet. Okay. We're thinking how many if I was talking to David about this, how many examples would I have to give him with details in before he goes, okay. I get it. I understand. A lot. Yeah. Ideally ideally, you'd hear some examples coming back from you. Okay. And then I I go, yeah. That's how it works. So because the rules are so nebulous, they have
23:10 lots of possible inputs, lots of possible outputs. It's really hard for us to tell if we've both understood the rule. If you've got an example that's got concrete detail in, we both run it through our heads. We both run it through our understandings of the rule. And if we come if we both say yes, that's how it should work, then it kind of tells us we've got that synchronization. Yeah. Yep. Okay. And the other thing that's important, especially in a big group, is we also capture questions. So a good even with the two of us,
23:46 this this is more applicable in a big group. But often you'll have a question and then you'll think, I'll ask about that in a minute and then you forget. So it's good to be in the practice of writing down your question, especially when people start doing it. I like them to only be able to ask questions that they've written down, which is a bit of a training wheels thing, but it gets you into the practice because if there's five of us and three people are talking, the other two can be writing down the questions that are coming up, you
24:12 know, and buffering them up fully. So that's it. Let's talk about your feature. What's your feature? Or you can start with the rules if you want. No. No. No. No. So let me I wanna because thing just just to look When we say what is a feature. Right? I mean, I can tell you the feature of the whole as an application as a whole, right, is to continuously fetch and update a GET repository locally, publish and changes as they happen. Now that's quite large. So I'm assuming the feature level has to be a little bit deeper than
24:15 Mapping our domain
24:55 that. So I'm thinking, okay. Does that mean that our first feature is it can clone a GET repository? Is that the first feature? Or is that too too low? I have no idea. They're actually something that comes out of maybe I'll phrase it like cloning a Git repo. Now feature really is just a way for us to bunch these things together. And the more I've done example mapping, the more I start to think of features as a sort of logical grouping of business rules that all need to be implemented together for business reasons. Okay. So that chunk
25:08 Example Mapping: Initial Repository Clone Feature
25:38 of work. So it's related to that user story thing. Right? The feature is the thing you might have in your sprint backlog or on your Kanban board. It's like, we'll do these bits together. Okay. Okay. And So why is that for as small as possible is a good practice? So calling a GET repository as as a feature in this example mapping context. Right? Yeah. If it's something you would want to do on its own. Yeah. I think I mean, well, it is how I started breaking the code. The first part of code I wrote was I want this to be able to
26:14 clone a repository. There are a couple of rules, which I'll try and guess now then. So the first rule is, as the directory already exists, don't clone it. Okay. So we need to sort of figure out what level we're at. So is that like if it's already been cloned? Okay. So there are a few different semantics here, I guess. Right? So, I mean, we could go through the code maybe or I can just try and remember it. But what happens is when we say, okay, start get sync. The first thing it does is says, okay,
26:51 am I configured to pillar repository? Let's assume that is yes for now because I don't think that's particularly interesting. And then says, okay. Does the directory that I wanna clone this to exist? If it does exist, then checks that it's a git repository. It checks that the remote match is what we are expecting the remote to be. So it will validate that the repository cloned as a GET repository and as the GET repository that we want to clone. And those are the two checks that happened. Alright. So there's an example where directory my projects exists
27:26 but isn't a Git repo. Correct. What what happens? Actually, it will print an error and exit. It gets synced when you run periodically. So you would embed get synced into your own application, which would run every five seconds and try to fetch updates for that repository. Alright. Because This is a startup rule though. So if if it can't, if that directory already exists on startup and as in a repos a GET repository, there just won't start the reconciliation loop. What happens if it is a GET repository? It will then inspect the remotes and check for one called origin, which matches the configuration.
28:14 So it's basically saying as that's the repository I'm supposed to be monitoring. So you won't clone it? No. If that check passes, it will enter the reconciliation loop. If it doesn't pass, it will exit with an error. So Are you regretting your decision to join me on this stream now? No. It always feels like this in the news because I'm having to to level up, but I'm I'm just helps me to just focus on typing things in and just type in what I'm listening to until I start to understand it. So I'm gonna ask some questions. So
28:56 but but enter reconciliation. Yeah. Reconciliation a little bit, I think, is another feature, but I'm unsure. We actually have a comment from Bradley who says, so as the feature just gets synced and not cloning a repository. I don't know. I'll rely on Ketan. So I would say that of all these things we're talking about, the feature is the thing that's most sort of arbitrary. Send me the feature as first clone or get repository, then we enter the second feature which is the reconciliation loop. Would that be two different features with two different sets of rules?
29:35 I we say don't worry about it. Okay. It's just the way of bundling some rules together that we're gonna talk about as one thing. And, ideally, it's a bundle of rules that I mean, if you wanna go down that route. It's nice to have a feature that is adds a capability. So it allows allows some entity to do something they couldn't do before or stops them doing something bad that they could do before. Okay. No. Can I can I throw some questions at you then? Yeah. So, you know, assuming other people are watching this because they wanna be able to do
30:06 Discussion: Depth of Detail and Audience
30:11 their own example map. Right? I'm assuming they're gonna have some of the same questions that I've got. Now the one that's set at the top of my head is, should I bog myself down with what the features are? Or is, you know, could I just keep listing examples and then kind of segment them later? Is that another approach for doing this? You should. So that's a really good question. Sometimes it's hard to there's two types of people. Some people will come in if you put a business person on the spot and say, right, you have to write
30:44 the requirements for this IT project. That I mean, don't do that. But that's what most people do. Right? They say, you have to submit a thing to this person by Tuesday to get it on the sprint and all that kind of nonsense. Some of them will think through very clearly what are the rules. And often that's in a domain that's very rule oriented, like maybe accounting or law. So they'll be able to write down things like you know, if it's something that's really familiar to them, they'll be able to write down. You can't issue issue a credit note without
31:13 it being attached to an invoice that hasn't previously been credit note. You know? Something like that. Yeah. When you're reading those and then put it in a load of paragraphs in Jira or something. But when you're reading that, can sort of pull out the rules. And all you need to do then is make sure you understand the rules by giving examples of how that you then generate some examples, and you're just checking that you've understand you've understood the rules that you spotted. The other way around is maybe it's in a quite complicated situation or there's somebody who doesn't have a huge knowledge
31:48 of the domain either because it's new or they're new to it. They might say, I don't know what the rules are, but but if you ask a specific question, what should happen in this situation? They'll be able to tell you. Which is like the examples first way. So we certainly can. There's only two of us, so we're not the conversation isn't as busy. But quite often, if you hear an example, just write it down and don't don't allocate it to anything. And then later on, we'll figure out maybe that example has told us about some new rules. You know?
32:21 Alright. Okay. But the end but the end state is we want the examples to be scoped to rules. So sometimes you'll have an example and you'll realize there's five or six rules being applied, and you'll have to kind of decompose it a bit to make it clearer. You'll have an example that's like the whole the story of the entire system. Yep. And you'll kinda want to split it up a bit. So let let's go back to what we're doing. Okay? Yep. I'm doing that question. If the repository was already cloned, don't clone it. So that makes sense. The directory exist,
32:54 but isn't a Git repo. Show an error because you can't recover from that. Yep. If it exists and it is a Git repo, we'll do a thing called reconciliation next. Well, yeah, we've messed so we also had a director as a Git repo. It's not the rate queue. It's not the correct GET repository. Okay. That should error. Right? But it could be a GET repository, but when we analyze the remote, really, oh, this is actually some other GET repository. At which point, we can't do anything. And what's the rule about whether it's the right reconciliation
33:42 whether it's the right one? It has a remote So if we imagine that when I'm I'm gonna add up like, I I like my own color. So, I mean, if we imagine that the way I start this would be repo as is let's just say, github.com/rockcode/rockcode. Right? And that's gonna clone to a directory called my clone. Now if my clone has a remote that hasn't github.com/rockode/rockode, then I just have to enter and exit. It doesn't have a remote. Well, it will have a remote, but not the remote we expect. So if it has a remote with correct
34:25 URL? Yeah. You can see. Yeah. I don't know what URL could be incorrect or correct. Yeah. And with a different rule. Yeah. So we we talked about didn't we the the kind of level of abstraction. Something that is maybe important to talk about is that different rules happen at different levels of abstraction. And it's important to capture all the rules you've got, but some of them might not be ones that the business people care about. So this is one where a sort of you could you could sort of say that this this rule that I'm wobbling
35:01 about what makes it a correct remote is a sort of sub rule of how reconciliation loop is gonna work. Does that make sense? Well, we wouldn't enter the reconciliation loop if the remote was incorrect. So those are the two checks that happen on startup. First, as a a directory with a Git repository and as the remote correct. And then we enter the reconciliation loop, has all of its own rules. So is that example in the right place? Would you see that as part of the Oh, no. I just stole that card. Okay. So I alright. Okay. So we're we're putting these
35:39 under okay. Yeah. That's my bad. Right? So that example should have been under the other rule. So so this thing about this this rule about what makes it a correct remote. So we've got this concept of it's not the correct repo. Ah. And we're going into what may we could go into from your little sticky there. We could go into what makes it correct repo and whether to go in whether to dive into that in example mapping is something where you sort of read the rule, read the room. So is that something only the devs are
36:10 interested in? Yeah. I think so I'm trying to understand what you said there. I think there's something really important there that hasn't been exposed yet. You're completely right about it. It's that I have this knowledge in my head that says, it is not the correct git repository under this circumstance. And the circumstance is the code is hard coded to check for an origin a remote called origin. And maybe that needs to be explicitly stuck in somewhere. But So It's it's like, do you want that to be the the reason you're writing these scenarios is to communicate with future people
36:41 using the library. So do you want to be explicit in the Gherkin? Well, yeah. I think it should be because if people want to use an existing remote that I don't that hasn't provided or hasn't cloned by the library, then they have to understand there's a rule that says that is possible if your if your remote is called origin. Yes. We could go out some examples quickly. Right? And I'm just gonna do notes. So when because it's a workshop. We're just doing notes that are gonna remind us later of what we talked about. Okay. You don't start writing out given when then
37:15 in this. But you need enough detail that when you look at that note later, you'll have a good stab at writing out the detail, if that makes sense. So, oh, Dan North has this thing. It's like the friends episode names. Yep. So it's like the one where has origin but wrong URL. That's enough to remind us later when we're reading this. Right? It is. Has no origin? Yep. Remote? Yep. If we want someone might do that. What else? Has origin pointing to right URL. So it's okay. Can you think of another edge case? So the testers are good, actually.
38:17 No. I I I think the rule is, I mean, it's a relatively simple rule. It's just good that we documented that, and it's either right or wrong. And I think or missing. Yeah. I think you've kind of documented the three existential states of a remote, so I'll go with it. So this has two strategies for finding more examples. I'll I'll just talk about them here. One is context questions. So you look at an existing this is when you've run out of ideas and you wanna generate some more stuff. So you look at the context and say,
38:49 is there any context where it let's can you see when I click on something? Yeah. Can. I can. Yeah. It makes it kinda shadowed. See that one? The directory exists but isn't the Git repo. Don't claim it in it. Error. So any situation where so the context question is, is there any situation where there could be a directory that isn't the Git repo and we don't error? No. Okay. So so you can ask questions like that. Right? So this one, is there any situation where it would have the origin and the correct URL, but we wouldn't
39:30 consider it the correct remote. No. And maybe this again. Is this something that's in my head that we have to get on a sticky somehow? Because the reason that the program will error and exit if it's not a get repository or as it hasn't got the correct remote is that this library is quite destructive and that it I mean, the way that it would proceed would be to remove that directory or start pulling the ref log over the top of it. And, like, you don't I never wanna be in a situation where anyone not I I can't say lose these data,
40:07 but if someone made a manual commit to that repository and then I command and revert it and, you know, reset and force pull or something like that, then I definitely put them in a situation where it's gonna be a lot more work to get that back. So the the rule is always just and my head has been, well, if it's not what I expect, then I can't proceed safely. Exit. Yeah. Kinda where I am. Yeah. Okay. And you should you should definitely capture that sort of thing now because we're trying to do this in a cheap way. I know we're
40:30 Discussion: Capturing Rules and Evolving Design
40:36 we're talking through it because it's a stream, but we're just capturing what it's gonna do now. Or if you're doing this at the start of your sprint, we're just gonna capture what we're gonna do next. Yeah. Okay. It's gonna get committed to the code base maybe as feature files, but they're editable. So if it changes if we change what it does, and actually we have some way of recovering from multiple directories, we'll change we'll change the features then when we get to it. Yeah. I mean, someone could come along at some point, I guess, and add strict mode to this,
41:06 which just says, well, I I don't care what's on desk. This is what we need in this location, and we're always gonna blow it away and make sure it's always the get remote and no local changes. Like, that could be a valuable feature eventually. Yeah. Yeah. And that's maybe a difference between the things that live in Jira, user stories or feature requests. Right? Or or other ticketing systems. And the thing that lives in your code base in the feature file. Because one is like a change a set of changes, and the thing in your version control is like, this is actually
41:40 what it does right now. You can read this to find out what it does right now because we're automating tests off it. So here's here's a truth about the system. If you're looking at, know, looking through old Jira tickets, you can often find contradictory things and you have to work out which was the label which one was latest because that was the one that got well, things get changed over time. Well, I can contradict myself in a way because, like and like I said at the start, but I'm actually extracting internal code, private code that we have within
42:09 our organization that I wrote to do this job and I wanna make it a reusable library for others. The one we use internally runs in that strict mode. It it will do a reset and a clean on that directory before it does every reconciliation. But that's something that I'm personally willing for it to do, not something I'm willing to Yeah. Make you know, release open source at least in the first version. So Yeah. You don't want liability. Exactly. Right. So what else happens? So we're talking about cloning. What else happens in cloning? So this is other strategy.
42:39 So we talked about context questions. Is there a context where this examples have an action and an outcome? Mhmm. Is there is there a different context where that action wouldn't cause the outcome? That's the context question. And the outcome questioning is her other sort of line of a of a approach that she talks about in a blog. As well as that happening, what else happens? So as well as erroring in these different cases, is there anything is there anything else that happens? Like, is there a log or is there when exiting, is it when we say error, maybe we should tighten
43:14 up. When when it says error, does that mean exit? Yes. So we print the standard error saying directory exists, but as a GET repository or directory exists as a get repository, but as an incorrect remote, and then we exit we exit with a non zero code. I don't know how much of that we capture in our example. So I think that the audience. I mean, this is I guess, this is another thing that's tricky for me is that, Jen, you know, I can I can understand the level users of a blog want to interact with it? This is
43:46 a very developer centric library. So I guess, going into implementation details has the right level of abstraction maybe because developers want to know, alright. Does x is zero? Does x are not zero? Like, maybe that is valuable information. Yeah. Writing for the unknown audience is always harder. If you were if you had a group of developers who were gonna be using the library and we're and we're they're in this session with us, we'd naturally find that level of detail because you you can tell. You can tell when you've gone too deep because no one cares about it.
44:21 And and the the trick, if you're facilitating this kind of session, is to recognize that's happened and say, well, let's talk about that when we're building it and you can write some unit tests, but we don't need to have that in the in the example map. It's not part of it's not part of the I think the thing is you don't have anyone who's giving you exceptions criteria. Right? There's no one who's it's you're doing both roles. Oh, yeah. I mean, I have some internal voices that I try and listen to. You know, they've all got different ideas for the
44:49 features. I'll try not to pluralize that again, but, you know yeah. I I this is something that, again, I built internally, and I'm just trying to extract. So there's you're right. There's no there's no users. There's no audience yet. I'm making a lot of assumptions. But if you do have that kind of internal customer, internal stakeholder, product owner type person, you can tell when you got to the point where they're like, yeah. I don't care about that. Whatever you whatever you think. Right? And that's the level you sort of cut off at. Because why because? Because it gives you a lot
45:21 more freedom of decision making as you build it. If if right. Just as an example, this this first rule Yep. Doesn't say anything about exiting or errors. So if we're in the conversation and I and I've got, like, some if if this is something we're gonna sell, right, and we need to get someone to approve it, and we start you and me, the dev, start talking about, like, alright. So should it exit with a non zero exit code? How much detail should we display the message? You would tell someone would say, I don't care. All I care about is that it doesn't
46:05 clone the repository. And that gives you a really useful heuristic that we're not gonna need to capture all that stuff. That's something we can talk about when we're building it. And I can decide whether to implement that. And, you know, with my selfish developer hat on, if I run out of time, I can do a really basic version of it. And if I've got loads of time, I can have a really amazing thing where it, like, outputs it in a format that's got nice colors and, you know. Yeah. So I I get a lot more flexibility
46:33 about how it's implemented and actually in an agile context that makes it a lot easier to fit things into sprints. Because if you're running out of time, you do it. You know what the rule is for it to be acceptable to the business. So you can cut corners or you can do a really great job if you think there's extra value to be had there for the for the users. You can do it even better than we talked about. But if you if you try and do it all in the conversation and you try and get all the detail, you're kind of
47:05 locked into one specific implementation. Now this isn't a UI led project, but some stakeholders won't care what the page looks like, or they won't k they won't care what the interface looks like in the admin area as long as it does the thing they want. They they might trust the developers to do it. Other teams, they might want to see a wireframe. Okay. Again, you're discovering the level of detail in UX to go into. I I try and stay away from UX stuff here because text isn't a good medium for doing user interface stuff. So
47:42 example mapping you can capture all the business detail and then maybe say, okay, we have to do some wireframes. Okay. What what so I'm gonna get stint and it's gonna clone the repo and we've got these rules about whether it'll whether it'll clone the repo or not. Yes. Yes. We do. What else? So assuming that we pass the rules at that stage, we then enter the reconciliation where there are some more rules. Yeah. It's it's near the same. So do you wanna call it a different feature? I think reconciliation has a different feature. Yeah. I think there are two different stages of,
47:50 Identifying the Reconciliation Feature
48:24 like if if this was a state machine, that'd the first stage where there's which is get prepare for reconciliation, and then there's reconciliation. So I think it's probably a new feature. So we've got our first feature, which is clone the repository, and now we're in the reconciliation loop feature. So I guess we can just change this to yellow. So did we have an exact yeah. Go for it. If we had an do we have an example that's we skip in in the initial clone? Have we got one more it's there and it's got the right thing, so we just
49:00 carry on. No. We don't. So we do nothing and do reconciliation. Now with that reference, the other row and just the Yeah. Directory as correct repo, so that means we can move, continue. Yeah. Repay is already closed, so they don't need to clone. Okay. Repay is already cloned on disk, so we don't need to clone. Something that we don't really get in example mapping is a time series of steps. So you'd normally sort of have to overlay something else on that. You can use user story mapping. You can use event storming. Okay. You've got a long running process that's a series of
49:52 steps. There's no sort of hour of time in the in an example map. It's an assumption that we we want to understand the rules. But if if it helps for you to have those rules in a context of time, There are other formats. There's also feature mapping, John Smart's feature mapping, which is a bit like example mapping, but it does have that hour of time. So it's it's kinda halfway between this and event storming. Okay. So this this is suitable where everyone's kind of understands the steps. Imagine ecommerce. Right? Everyone knows I put it in the basket and
50:27 then I check it out. Yep. And you can talk about the things in isolation and say, well, what are the rules of what you can add to your basket? So tell me about reconciliation. Okay. So once we enter the reconciliation phase, that just means that on a configured interval, we are going to attempt to fetch all updates to that repository. And we can talk about which branch we're looking at. Yeah. It's part of the configuration, which I kind of omitted, I guess. So the library expects you to pass in a target, which is the branch or tag name,
50:38 Example Mapping: Repository Reconciliation Rules
51:16 the repository URL, any authentication that's required. I mean, we can talk about what that config is. I don't know. So does that affect this thing about the being a quick repo? No. Because the remotes are would be configured regardless of target. Yeah. Okay. So as long as the origin's there. As long as the origin matches the URL that we expect, then we will attempt to fetch on the target from the configuration. So branches is gonna be something we think about as part of reconciliation, maybe. It it will be for sure. Definitely. And and authentication. Because so far, like, the code
52:03 I guess, authentication is important for cloning. But if we have a directory exists and the remote is correct, so that we don't need authentication at that stage. So that is the reconciliation that we do. Maybe that needs to be documented. I'm not sure. But maybe we should tackle some of the reconciliation first and then handle the off. I don't know. Yeah. I'm just putting I put that question there so we don't forget. Yes. Sure. By the way, it's perfectly fine to come out of example mapping with some unresolved questions. That's natural. Yeah. I guess for for today's
52:34 example, we can just assume that the environment provides us an indication that we need, and we can have both that one later for sure. Yeah. And just in life, you know, you you you talk about it and there's a question that we don't know the answer to, we have to go and ask someone. And if you're in a situation where you're deciding whether you whether you're ready to start work on it, you can decide whether there's too many questions or not. If you saw me. If you got something that's got loads of red questions on, might say, let's not do
53:05 it today. If you're doing like a scrum thing, you might say it's not ready for sprint planning. If there's just a couple of if there's one question that seems minor, you can sort of say, oh, okay. Well, we trust you to answer that. I guess that's just an exploratory thing and to understanding the problem. Yeah. It's very visual to see all those red stickies and be like, oh, maybe we're not ready to maybe we don't understand this enough yet, I guess. Yeah. Actually, if you zoom out a little bit, you can use the fit to width thing.
53:39 What's the fit to width thing? What's that? In the bottom right, in the menu, in the map thing, the the sort of arrows either side. Yeah. Yeah. Fits the screen. You can sort you you're right. You can sort of see from the shape what's going on. That first feature, what do you think jumps out? Think it's quite nice. It's easy to see there are two sets of rules and multiple examples per rule. I don't know if anything jumps out at me. Yeah. I think maybe too many examples. It looks it makes me think that that
54:17 rule could be decomposed into more than one rule just from the shape. Okay. So we could let's not do it now, but we could split it into a rule about the directory. Doesn't clone if the directory's not a git directory. And we could have separate rule that was like it doesn't also, another rule is it doesn't clone if there's a directory that's a git repo that doesn't have the right remote. You know, we could split in this into those two, and maybe that would be clearer for a reader. That's the only reason for splitting it.
54:53 Okay. That's not the only reason for splitting it. The other reason for splitting it then is if you've identified these two rules and you're in some kind of, like, prioritization backlog scrum situation, I. You haven't done the work already like you have, you you sometimes say, actually, that thing where the directory exists and it's not a git repo doesn't happen very much, so we can build that later. And then we can if you the rules tend to be the things that people care about being delivered in batches. Yeah. And this kind of vertical column thing
55:26 is really useful for either deferring work or splitting things down. Okay. That makes sense. You know, what would happen if we didn't implement that rule about error with a nice message? Well, it'll just crash because it tries to do a clone and there's the thing already and git will do an error and it'll be an ugly error message. That's fine. We'll we'll do the bit that catches that and, like, pre checks and does a nice error message as part of sprint four instead of sprint two. So, yeah, let's go back to talking about reconciliation. So it fetch fetched all the updates to
55:59 the repo and configured into it. What's the happy version of that? The happy version is that there are no local modifications, and it can do a fast forward pull or a fast forward fetch and merge, I guess, into the directory and publish the changes. Probably should I mean, that that's a happy path. It should be 90 plus percent of every run this does. Right? You know, people are pro I would hope people using this library and especially myself as a user of the library. I'm running it in an in an environment that wouldn't accept local modifications anyway.
56:44 So, you know So I'm gonna split this actually. Feels like there's two things we're talking about. Fetches happen on configured interval. Because it sounds like there's a concept there called a fetch. There is. Yes. But where is it configured? How is it configured? Via TOML or or command line argument. Okay. So configured. And what happens? It's just it's just running and fetching like a daemon. Yeah. So, I mean, I I tried not to mention this so far, but I keep completing my examples here. As a getSync gets exposed as a library and then a binary. So, you know, there's
57:27 those two different modalities. But I'm gonna try and con try and stick to using it as a library and forget. Because if we get the library right, the, the CLIs, the binary is fine. So you you when you initialize the library sorry? So that's very common. Right? Most software is a stack of things. And if you write if you write the examples express the examples and the rules in the right way, you they apply to both situations. So if we can phrase them if for a website, if you pretend that some people are on mobile and
58:02 some people are on the web and some people are talking to it through Alexa, you can see you can get a point where you're phrasing it in a way where the business details there, the detail that's relevant to the rule is there, but all other stuff isn't. Yeah. So if we just say configured with an interval of five seconds, then that just means every five seconds, the reconciliation loop is gonna run. It's gonna fetch the remote and try to do a fast forward merge with the changes. I I guess the simplest example actually would be it runs and the remote says there
58:34 are no more updates. So it doesn't do anything. That's it. Let's just talk about configured fetches as one thing. Right? Yeah. And then it fetches all updates to the repo. What's the example you just gave? No remote, no local or remote pods. Okay. Yeah. Nothing happens. So this is when a fetch happens. Right? Correct. So if there's no local mods, it does a fast forward Well, after no local mods, it will attempt to fetch. So, I mean, if there are local mods, it won't even attempt a fetch. What will it do? It will return an error and the reconciliation
59:22 that will exit because we don't want to blow away people's local changes. Okay. So I think that sounds like a new rule. Right? Exit yeah. Exit the loop if and the the the number of examples you're giving is a good sort of guide to that. Okay. So you So after our local modifications, then we just say, You've you've done something we don't expect. We're not gonna blow away, so, you know, good luck. Go fix it. And it's up to the user to go and either commit or revert those changes. And the the thing we're talking about in
59:57 terms of prioritization, right, you could decide not to do that yet. Yeah. Correct. We'll write it in the manual. Don't do that. And that would get on the fine behavior if they've made some local mods. Yep. So, you know and the way you sort of do that is you you'd sort of say, well, okay. This is a different feature. We're not building out this sprint. We'll call it yeah. You put another yellow sticky there and say, we'll call this local module function feature, and we'll have a separate Jira ticket for it and, you know, all that jazz.
1:00:31 Okay. If you if you do all that stuff with a different pull request. Yes. So you could implement the fetches happening on a configured interval. Right? You could just you're implementing it, you can just say, well, I'm gonna do this configuration thing and I'll just make sure it calls some function in five seconds if that's what's in the config file. Yep. And I'll commit that and pull it push it because it doesn't hurt anyone. And then, you know, then do the actual fetches. So that's yeah. So no local remote mods. Nothing happens. No local mods does a fast forward pull.
1:01:07 Yep. Are there any other is this is there any situation where you'd have no local mods and it would do a merge or something else? Yes. So that there's I said fast forward very explicitly because there are, I guess, two other conditions. It could be that when we fetch the remote, that history has been rewritten, and we are not actually able to do the fast forward merge, we would error in that case too. At least for now. I mean, I think there may be features in the future where we try and handle that better. But for now, get sync really because the
1:01:45 simplest path is just to expect a linear history and get like, this is supposed to be an automated system. We don't expect people to rewrite the history, at least initially. And I guess that could be like the branch was deleted. It could be someone did a rebase, someone modified a commit message. It could be I mean, there are many examples of what would break fast forward merge. Right. So and you're not gonna club in the local if the remote's changed? Yeah. I want to add her and say, hey. The history here has changed. We expect you to go and
1:02:21 blow away that directory and we'll clone it from scratch. We may provide a flag that does that automatically, but, again, I'm trying to do this the safest way first and then add on overwrites. Yeah. That's a good example of, like, you might do something different in future and you'll come back and take this example out. Yeah. Exactly. So what else? Are there any other situations? Let me look at them. So when there's no local remote mods, nothing happen nothing at all happens? Correct. Nothing else happens? Yep. It goes back to sleep till the configured interval then whoops.
1:02:56 And we're always gonna try to do a fast forward pull. It will only accept the fast forward fetch and merge. Yes. So I that's a yeah. A pull would be a fetch and merge. So I mean, there are things that happen when there are those changes. Maybe we need to bring in those. I don't know if that's a different feature or not. I'll trust your judgment. But say there are four commits that branch, I guess that's unlikely depending on the interval. But say there are four commits, it will then work out the depths of those commits, look for all the files that
1:03:29 have changed, and it send it publishes a message that says, hey. These files have changed to any number of subscribers. Okay. So does that sound like maybe I well, I think what what what I would tend to do is have that as a rule here. And if the feature's looking wide, I think we'll are some of these things a different thing? Okay. So what what pull reports. So yeah. Yeah. Pillar ports. I I mean, I don't know if I'd use that vocabulary. I don't know if that's important. But It is important. What would you call it?
1:04:09 Go on. You type it. I I thought I just got to watch. Right. Okay. So changes to Rupo are published on file level. So it's not patch level. We only inform you that if fail has changed, not which lanes, not which blobs. None of that is important. The idea being that whoever signs up for subscriptions can say, I want to be notified of changes within this directory. And that this is this is future work. I'm not gonna get into too much. But, you know, you can say as a consumer of this library, here's my subscriber. He wants
1:04:48 to know all YAML files in the directory change. But the the first one is just, hey. Here's all the files that changed. So is that right? If I put four commits to feed dot text, I get one notification that feed dot text changed? Correct. Yes. And if I put one commit to feed dot text I'll do YAML actually because if that's more like your use case. It's good to match these cases. Yep. Two notification. I get one notification with both files or I get two notes That's that's a good point. So right now, you get one notification
1:05:30 that has multiple objects in it. So it'd be a list of files that changed. Yeah. Yeah. So one notifications would What so why have I put a filename in there? Just because it's The filename is what will be listed in the in the change set. Yeah. So as long as that's understandable to everyone. So if nothing's pulled, do you get a notification that nothing changed or do you get no notification? There would be no notification. Nothing would happen. So, you know, whether this is a different feature, we've talked about it a few times already, but it just depends on whether you feel
1:06:21 like you're gonna do it all in one go, whether that's gonna be something you might do separately. Which is a sizing thing. It's that feature scope. Right? If you're a team who are trying to get everything down to a day, maybe you'd use that as a nice scene along which to split your feature. I mean, I think I would. I mean, I'm already thinking about this from a code point of view. And I think, you know, I really wanna make sure the reconciliation lip is solid and tested. And that just means I probably would have notifications or pops up
1:06:53 as a new feature afterwards, but I'm happy with the reconciliation. So, yeah, we to move over. And even within the features, I would tend to work on these rule by rule sometimes as as a way of organizing my workflow. So that's probably enough to talk about for now as an example of how to do it. Sweet. How long is that? Four fifty minutes? An hour and ten. Yeah. But we chatted for a bit at the start. Oh, we did. I thought you made it a total. Alright. My energy is dropping. Yours might be as well.
1:07:33 That and that's why I sort of say half an hour. It would be better if we talked about this first bit one morning, and then the next day over the lunch, come back to it and say, you know, what what else should happen. Yeah. Yeah. Sure. So and then then if if you've got a couple more minutes, what I so I'll make this board I'm saying that's enough. I just make this public to the world? So, yeah, I can. Okay. Cool. So I'm gonna go and split that first rule. Okay. So in the show notes, I'll I'll
1:08:06 put a link to this board if anyone wants to follow along at home. I'm curious then before we finish up. It's like, you know, I I I wanna start getting this library moving forward. Right? And that's just been really good. I've taken a lot of the knowledge that I had in my head about how I want it to work, and we've now got that with rules. What is my next step then as a developer? Do I start writing scenarios and feature fails to map to these features? Like, as as each one of these yellow post its
1:08:36 one feature and then a rule as a scenario? Is that a fair mapping of what I what I do next? Yeah. And so very specifically, these map to Gherkin syntax. And what we could do what time do you wanna finish? Half past? Yeah. Roughly. Later? That's flexible. Whatever you've got, mate. So let's write some of it out as Gherkin. I'm just editing the which is a bad thing to do. Just to separate out that big one with the directory. What what if refactoring is important. So did we have the happy version? No. The on disk,
1:08:46 Transition to Gherkin Syntax
1:09:37 so clone the remote. Correct. If it actually exists and isn't a git clone, error. Not clone. Checkout. Checkout. If the directory is Git, but it doesn't match the remote, don't clone. And I think I think all of these are examples of that rule. I think this is the same rule. Okay. It's just the definition of what correct is. So Does that make sense? I'm gonna get rid of that. Yeah. Makes sense. So these are all explicitly error error versions. Yep. It's often good to go run through at the end of example mapping, do a bit
1:10:47 of this. And it is a bit easier. It's a bit easier in person because people are willing to move stickies around. And it it's easier if you're in this kind of session and there's loads of people familiar with it. I think the first time you do it, end up with one person being the sort of owner of the board. I think I've done that a bit today. And he's the only one typing. So if you can try and break that down a bit if you're doing this in groups, that could be productive. Know? While while Dave
1:11:18 and I were talking about the other stuff, someone could have been coming back and clarifying this to their own sort of satisfaction and then calling you back. So So that's probably enough output. Which feature do you want to quickly do in in Gherkin? Well, I feel like the initial clone feature will be the the simpler one. But Are you able to do some sort of split screen with the IDE? Yeah. Sure. Oh, yeah. You're a Mac user now. I am. You've joined us. Oh, I'm trying to be. Alright. So there we go. So delete that example.
1:12:17 Delete? That's how it worked. Undelete it. Let's look at it. It looks like a rule to me. Oh, no. Maybe not. Maybe that's an example. What I wrote was yeah. I I think I tried to encapsulate everything in a single example as if I was trying to write, like, one end to end test, which it looks like was necessarily the correct approach. And then I that's as a rule, I think that I got a little bit confused. This is yeah. If there's nothing there, then this is what I expect to happen, and then I was gonna try and layer
1:12:48 on the the edge cases after that. So try comment that well, we just start from what we've mapped. Feature? So this would just match this. Right? So initial colon of the repository. Right? So you're you're expanding on stuff because we it's just scrappy notes from your conversation. Generally, one or maybe two people would go away and write it up. And who does that? I like Matt Wynn's heuristic, which is whoever learned the most. So what we're gonna do is we're gonna write this down and then we're gonna we're everyone who's in the meetings here, but we then share it
1:12:57 Writing Gherkin for the Initial Clone Feature
1:13:32 back and say this is what this is what we talked about. And if you do that on short enough time scale, like day hours or days, it's really easy for the people who were in the conversation to read through it and just go, yep. Yep. Yep. Yep. Yep. Yep. Yep. Right? Yeah. Because it's it's already loaded into their head. Okay. If write it all down and then six months later, show it to someone, they won't know what any of it means sometimes. Or they won't be able to tell say, yes. That's what we talked about.
1:14:02 So it's important to have that feedback loop. Whoever feels like they learned the most, so maybe that would be me, would then write out the examples with more detail, expanding on it a bit, show it to the people who had the more knowledge at the start. And if they if they say, yep. That's right. It's that's an important validation step that I understood it because I was able to sort of talk it back to you. I do that with my daughter. If I tell her if this is some important instruction, I ask her to say it back to
1:14:29 me, like, to make sure she went in. So you've got feature. There's you can now have what's called a feature narrative. You can have, like, a paragraph or more of stuff. Right here? The parser just looks for the word. The next line. Yeah. Anything that doesn't have the word example with a colon is gonna be fine. The parser will just seek to the next the next, you know, keyword. So that's a good place to put some. So would you call if we wanted the name, it's it's like bootstrap get repository. Like, maybe give it a
1:14:32 Gherkin Syntax: Rules, Examples, and Documentation
1:15:11 catch your name and then use a narrative to actually explain what that means. Yeah. When we first we need to check blah blah blah blah blah. You can put your as a so that we need to be able to If you want if you want if you've got that, don't don't create it if you haven't got it. And you can put random other things that people talked about, you know. This is for Barry in the accounting. It's just for some free text for the that's gonna be useful to a reader. Right. Okay. There's lots of places you can put
1:15:41 free text in a feature file, specifically because we're expecting them to be read by people as well as passed by robots. So, like, chart and street like that for now? Yeah. And now which cucumber are you using? Do you know? The new syntax stuff that we talked about a few weeks ago when I was curious. It works. Like, so So Gherkin six was the first one that added a new keyword for ages. Gherkin what? Sorry? Gherkin version six. Uh-huh. Okay. Cucumber Cucumber six was the first one that had a a Gherkin change for a while.
1:16:26 So you can put rule in because we've doing example mapping a lot. You can put a rule. So what's the first rule? Local directory doesn't exist. Yeah. You can phrase it as a rule though. What what you can put what you can put it on the sticky, you know. So repositories are cool and if not already? So we have in the blue. Right? Already on disk. You you're improving it from the workshop. You're making it into sentences. Oh, right. Sent. Okay. You know, this is potentially gonna be mapping on to some code. Okay. Now this should I I put a
1:17:19 new lines or it's all set on a line? Does that matter? All goes on the line. It's good incentive to keep it short. Okay. So I got a comment saying I am difficult to understand stuff, you know. Yeah. I I do have allergies and they're quite bad today. Sorry about that. Plus I'm Scottish, which means I'm not really natively speaking English anyway, but I'll I'll try and improve. So thanks. I don't know if we can support some Scots yet. It might do. You can contribute translations in the main Gherkin repository. So there's a it's a little language thing you can put
1:17:57 at the top of the file and it switches all the keywords into something else. So underneath the rule, you can have another paragraph if you if it's a really complicated rule and you want to sort of explain the reasons behind it, but let's not bother. And then you indent we've got one example, haven't we? We do have an example. So we would say no local. I keep trying to take this in pale case. I I don't know why I have this taxation with using pale case. I need to try and get over that. Because it's the happy path,
1:18:34 we could either not give the example a title because it seems a bit like the thing. Or you don't have examples don't have to be in a rule. So sometimes for short feature, I'd have the example just under the feature. Under the example, we have these steps. So given when then. K. So, Gavin, I have no local directory. When I when I do the thing. I don't know. When I sync the git? When I do git? When I sync the git, then I have a locally cloned version. I mean, I always really suck at this given when that stuff. So That's okay.
1:19:28 So the the important thing up here. So given I have get sync configured, so we're we're not talking about configuration right now, and our local directory does not exist. So I have no local directory. Yep. When I sync a get repository yeah. When I sync the get repository, then the repository is gone. Essentially, I think my language server is probably better. Yeah. Is that good? Yeah. So the the there are some things we could talk about here. So how much detail do you want to include? We could include the URL of the Git repository. And it really doesn't matter from in terms
1:20:08 of automation, and it really doesn't matter in terms of business agreement. It's purely, is it gonna add value for the reader to understand that that rule? That's the important thing to to to to do if you're using rules. Does it help understand that rule? Would a reasonable person understand that it's the same URL? It's the same repository that's gonna be cloned. So should we I mean, would we provide a table here with example configurations? Like, what the target directory like, we don't see what the local directory is. Does that mean that GET sync always clones to a
1:20:52 a hard coded location? You know, should we is it something that's passed in through a configuration, which means we have to make it very explicit in this example? Does that matter yet? Good questions. It depends. I knew you were gonna say that. It's about it's about the target audience. So would a re if you think about who's gonna be reading it, are they gonna assume that the git repository I asked to be synced is the one that's cloned to disk? So what I have in my head is we haven't in our example mapping spoken about
1:21:34 configuration. Now maybe that the first version test I released doesn't actually allow you to configure it, and it probably has a hardcore location, and then that's that's scenario doesn't care. And it may be that I come back later and add a new feature that allows them to configure how that that works. And then that those features and those rules and those examples will have something that says here's how it's configured and that's how it works. I mean, this is is that fair? Okay. Yeah. When you when you're on a greenfields project, most of the time you're writing new feature files.
1:22:08 As it grows, you start to be editing the feature files more often. So you might come back and look at this feature and say, actually, there's some more there's another rule which is we can clone to a place that isn't the default if it's configured. Or there could be, like, you add extra examples for if if a different location's given. It could be doing it in a different file. It's it's kind of optional. That management is really it's about how you think of these things in inside the business. Okay. So I don't think it matters then
1:22:43 unless this feature failed, that's rule in this example because we haven't explored the configuration element yet. Right? So Yeah. Yeah. And I think so so we could add in when I sync the repository at git sort of colon whatever. Mhmm. Then git colon whatever is cloned to disk. But I don't think a reasonable reader would expect anything different. Do you know what I mean? Yeah. That's true. Yeah. Think anyone would think you're gonna implement it, so I asked to get sync some remote and that you sync some different Git repo. So I think that's enough that's maybe enough
1:23:19 detail, but it's definitely a judgment call for for the author. So that's so what's the next rule? Well, we have a question if you wanna tackle that Yeah. From James. Is examples synonymous with scenario? He's always used scenario. Is there a case where one would use scenario versus example? Example is just more understandable to most people. They're actually implemented as synonyms in the parser translation. So implementing was quite easy. They just added an ex in English, it can be scenario or example. It's exactly the same thing, but just experience of talking talking to normal people.
1:24:02 I I I was working with someone recently who's got a sort of languages background, and she was referring to them as scenario I. Scenario I. Yeah. Which I think is good, but it sort of shows it's not really plain language. So they are the same thing. Okay. Great. That's good to know. Give me an example of how that would be applied than it is to say, what scenario you know, think scenario is more for people into tabletop war games like I used to be. Yeah. I think example makes more more sense to me. But rule feature rules and examples,
1:24:36 I think they apply better to me than what the previous syntax did. So. Yeah. And rule has been retrofitted in because it was so useful to talk about, you know, talking about these things in the context of a business rule is really useful. Okay. Agreed. I've heard of a company a big company in The US who are using this as a way of finding out what their business rules are. So they they kinda grip their feature files and they discover which business rules are implemented in their system. Interesting. Alright. So we have our first rule documented,
1:25:14 our first example provided. I'm assuming I mean, again, developer head on here. Right? Would I go through each of the example mappings, techies, all the different rules, and put that into a feature file? Or at this stage, would you go and start writing a test for your first rule and example? Or does that does not matter? Am I just overthinking that? At this stage, just after example mapping, I would write them all out, not worry too much about it being gherkin syntax, and share them. So if using a ticketing system, maybe you put them in there,
1:25:21 Using Gherkin for Implementation and Testing Strategy
1:25:49 maybe paste them into Slack, and get a thumbs up. I'd do that validation of the things we covered in the workshop. Then Okay. It's like then it's like, when are you gonna work on this feature? It's in your backlog and, you know Yeah. Yeah. Yeah. Yeah. Today, I'm gonna work on initial clone. I would then put that feature into the code base as part of my commit. Now you can use tagging. So I'd probably copy paste it all out of wherever I got the feedback and get it all into get here. But then you can tag things
1:26:24 as excluded. So all cucumbers have a sort of tagging system. So I would probably put how many examples have we got? One, two, three, four three rules and five examples. I've probably dumped them all onto disk is the first thing I did. And then maybe I'd put an at I don't know. Not implemented yet on the ones I haven't done yet. Except everything except the first one I'm gonna tackle. And then when I run Cucumber, I'd run it with yeah. Something like that. You can run it with don't run the ones with that aren't implemented yet.
1:27:03 Get one of the scenarios passing. So the tag can apply on the example or on the rule or on the feature, if that makes sense. It does make sense. Yeah. I mean, I think when I was you know, I've already written some crude testing for my previous one. So I think for now, what I'll do is I will type up all of the example mapping stuff into feature fails, not implement it, and just push that to the repository and then continue from there. Sunny? I tend to do it as part of the same PR. You can do it as a different commit
1:27:47 if you want to, but What do you mean? The test or or the the admin? Because you told me to commit all the feature fails to test first. So in my head, I'm like, okay. I should just go through the example map, write all the feature fails with the rules and examples that we have, Push that up. You know, I don't have a team. That's just an open source thing that I'm working on. But, you know, in theory, by being there on the GitLab repository, other people can see. And I and then I would go back and start to add
1:28:13 the actual test cases and the functionality at the same time. Right? No? Yeah. You can do that. I think I'm I'm more used to sort of inside a company, you'd be doing that sharing and getting feedback outside of Git. I guess GitOps, you'd be doing it inside Git. Right? Right. I guess, you know, if anyone wants to write some Rust code and find us to be an interest, then even just by me getting these feature files and pushing them to Git, I can say to them, I'm watching, hey. If you wanna learn Rust, this might be a good opportunity for you to
1:28:46 come and write code that would help me. So and Yeah. That's definitely true. And there are tools that sort help that. So oh god. I can never remember the names of them. So one called I really should know. So the Q can have a tool that will connect to your Git repo and expose the feature files in a nice explorable, commentable, editable interface that's so friendly for people who don't aren't really into Git. And there are other similar tools. So so there's definitely a sort of level of collaboration that happens. I I think for this particular domain, people
1:29:30 that are not interested in GET probably don't care about this project. But you're right for sure. We also got another sorry. I need to again. Yeah. Go for it. It'd be interesting to just look at maybe some way of publishing the not implemented ones in a specific location. Yeah. Open an issue open an issue for each one. Yeah. I should do that actually, shouldn't I? We also got another comment from James, which I just cut out your face while we're at it. But his favorite thing about Gurken is just the documentation side of it and not so
1:30:04 much the testing. And, you know, I don't know if I'm right or wrong, you're back. I don't know if I'm or wrong, but, I mean, BDD, they always say it's not about testing. Right? It's it's about the human aspect of it rather than the automation side of it. Yeah. And depending when you're having these sort of example mapping conversations, you might discover a business rule and then not implement it in software. It might be something the people using the admin system do. Right? So the rule is you can't mark an invoice as paid if it is
1:30:43 more less than a thousand pounds without more than a thousand pounds without approval from supervisor. We might might uncover that in example mapping. And then when we're looking at the practicality of when we're gonna build this stuff, we'll just say, okay. We'll write that on a email to everyone. Don't approve an invoice. Yeah. But maybe you want to document that as part of your your your your documented features and tag it with, like, at offline policy. Just good to know what the rules are sometimes. And then it gives you an an opportunity in the future.
1:31:17 Maybe you can automate it. Yeah. I I I think that's definitely the the same vibe I had when I started when I when I wanted to write the feature files for this. We really just expose the rules of what happens with the library when you use it. I mean, the test, yeah, that that would be useful. But really, it was just about documenting, hey, if you use this and the directory exists, the best way to find that out, what happens, or what the conditions are for it to continue operating successfully is just to read the feature files.
1:31:43 You know, this for me is very much the documentation on how this slightly esoteric library is gonna work more than the tests. Otherwise, I do want tests, but so Yeah. So let's talk about tests. Right? So just looking at this feature, you could do that in three commits. Right? You could you could implement cloning as assuming the directory is never gonna be there. Yep. Write a test where the directory is not there and sort of implement that first rule and, you know, call it a day. Go home for the day. If you've got some time, you can make the next one choose
1:32:24 one of the others. If the directory exists but isn't a git checkout, you can add a guard. And then implement the third one, which involves more git. But as you're working through that, each each business rule yeah. We mentioned earlier each software project is a stack of different layers. And where you choose to text really affects how the architecture's gonna look because the level you choose to validate something at, you're sort of constraining that the rule has to be implemented below that in the stack if you wanna look at it that way. So you've got your sort of core. You've
1:33:06 got your user interface y stuff, which in your case is a binary or it's library calls. Mhmm. And underneath, you've got infrastructure. You've got Git, which I guess you might have some interfaces around, you know, some abstractions around. Yeah. By narrowing down what part of the system you're testing, you're actually testing the rule. You you're constraining where the rule is. So if I let's take this first let's let's take the if the directory exists thing. If you choose to test that through the CLI and the way you would choose these different testing strategies is you'd tag it.
1:33:51 So you'd have one set of Cucumber tests. So one one set of Cucumber steps, like, you've got that implement that are running the CLI, and they but then they're filtering on the ones that are tagged at CLI or something like that. Yep. And then you have another Cucumber world, don't know what it's called in the Rust version. That's different implementations. It's it's testing it by calling the library with a different tag. And you run different sort of filter sets. Right? If you test it through the CLR Just wanna check if I actually set it up properly.
1:34:32 I'll come back to it. I'm sorry. I'm go. So this this rule, if the directory exists and isn't a git checkout error, where do we want that to be implemented? If we if we test through the CLI, it can be implemented in the CLI command, whatever it is. Yep. Or it can be implemented in the library. Or it can be implemented in git. So it could be implemented by Git throwing an error, and we're just catching it later. So that Yep. We don't know where the check is. That might be fine depending on the project. Like, as long as
1:35:06 it's in there somewhere, it's fine. If you want to ensure that it's in the library, then you should automate that test at the library level. Same scenario, but you automate it at that library level and then you know it's not implemented in the in the CLI. If it's a web app, you know, you check it at some by calling some service, and then you know it's not implemented in the controller or the template or the JavaScript. Maybe you want it in the JavaScript, so you implement it in the JavaScript Cucumber that doesn't have a real back end. Yeah.
1:35:37 But you're choosing for your test strategy where you want the rule to be implemented in future as well. Now at the other end, you've got the infrastructure. So how do you stop things being implemented with by a database trigger or stop things being implemented by just Git? Already has that behavior. You have some abstraction and you would replace Git with a fake in your tests. Right? If if you want to constrain it to just being implemented in that middle bit, which is more of a decoupled architecture. But but the testing strategy helps you design the architecture.
1:36:17 I'm writing an abstract for a talk like this now. It's good to if you see what I mean. On some projects, you might be super happy. You test the whole stack, and I test it by calling the binary. And as long as it's in there, it's fine. Your thing, because you want it to be a library, you should primarily be testing at the library level and then testing a subset at the CLI level just like a smoke test. Yeah. That makes that definitely makes sense to me for sure. So why don't And do you want it to be independent
1:36:50 of git? No. So you probably need git in there. Unless there's speed problem. Just It's tied pretty heavily to lib git two. I mean, obviously, we could say there's an abstraction where it should be able to support other version control systems. But I think with the equity of get, it's just not something at least in any short and medium term goals of the project for sure. Okay. So in that case, you wouldn't bother testing the fake git if git's fast enough. If you find your tests are going really slow because it's doing real git, you might
1:37:28 do it. But that kind of, like, architecture test strategy, you wouldn't deliberately replace Git. And it means that your business rule about the directory exists might be implemented by Git by LibGit. Yep. I mean, the CLI Git does throw an error if the thing already exists. So maybe you can just catch that exception from LibGit or catch that error. And you don't care where where in that stack it's implemented. So so each of these things, just think about what level to to test that to to make sure the rule's there. That's one of the key things
1:38:05 about rules is they're actually implemented somewhere. Examples aren't implemented. But rules have a kind of specific location if they're if they're fine grained enough. They're in a library or they're in a service or they're in a UX UI component. Okay. So let's let me see if I can recap this then. So we were using our example map and technique, and I think let me see if I can just stop splitting the screen actually. Let me go back to here. And we managed to, I think, quite well, go through a a large chunk of this library. Right? You know, we've got various features
1:38:28 Summary and Value of the Example Mapping Process
1:38:46 here. We're cloning our repository, called up this trap, whatever I want, or maybe kept that out slightly. Snappier name as a phase. We've got the rules here, and we've got examples. And just as our back and forward, as we were going through that, I think we began to share knowledge and understanding of what this this library was supposed to do, which I think is super invaluable. As far as the other features go, we talked about the reconciliation and what the semantics are there. I think what's good is documenting the knowledge and understanding we have of how
1:39:20 that works now is really important, but also it provides a framework for extension. Like, no other developers can come in and go, maybe we don't want to error when this fast forward is impossible, and then they can drop in new best of functionality. They have new stackies, which helps them build a new feature fails, new GERK, and and then expand that library. I I love this as a framework not for just document and testing, but just allowing people to expand the functionality as well. And then we talk about something that is really, really undefined at the moment, which is,
1:39:49 well, how do we expose us as a pops up system for people to then build their own tooling on top of this with whatever integrations you want. I think this document is really cool for doing that. And then we started to map that directly almost from feature rules and examples right into a get good. But I will get some test cases put up and and push us to the repository. But then we have a full thing from the stack is to human documentation to computer documentation, which is test. I really like that. Now is this something that you think we can
1:40:27 come back and carry on in the future when I've done a little bit of homework. I've hooked up a few of these components. Is that something you would like to do? Yeah. Sure. I mean, I'm not gonna be able to help you with the rest particularly. Well, maybe I could send you some rest at the same time as well. So Yeah. But generally, I wouldn't once you've written out the scenarios before you put them in version control, we tend not to keep the example maps around because they tend to lose meaning quite quickly because of the lack of details. Right now,
1:40:59 they mean a lot to you and me and maybe the people watching. But if we come back to them later, we might not remember what any of it means. So that's visual representation's not too important then? Like, I mean, what I'm thinking for is, like, the more feature fails we put together is good if you want to understand an individual feature. Is there not a way to understand the system as a whole? Just, like, say you were onboard a new developer, like, just to get them that really 10,000 feet overview. Maybe. Maybe that'd be useful to generate
1:41:33 this if people are really familiar with this kind of visualization, it'd be interesting to generate it from feature files, and I wonder if anyone's doing that. Yeah. But there's less there's less detail in this than there will be in the Gherkin because Yeah. Because it's up to it's just like, remember to write an example about this. So, normally I mean, if it's stickies on a wall, you know how that goes. That that doesn't stay too long. They fall off. Oh, yeah. Definitely. I can always take a photo of it and upload it to Confluence. Right? That's what
1:42:04 all professional teams do. Yeah. That'll work. Well, I think what I'll do is and they showed us, I will include the the example mapping for anyone who's watching, you know, in the in the future and and just wants to see what what I look like. Although, I guess, the videos are too, but still, you know, I'll leave it up online, but I'll try not to refer to it too much on my own and use the feature files. And I'll definitely adjust the Googling to see if anyone has wrote any tooling to generate maps from feature
1:42:32 files. I think that would be pretty sweet actually. Yeah. Alright. Yeah. I know there is there are some online example mapping tools. Wait. We're not using a specific one because we wanted to just make it simple, but yeah. Maybe some of them work in reverse. And, yeah, let's follow-up. Be good. Alright. Well, thank you again for donating even more of your time and teaching me all of your your magic. It's been very useful. What I'll do is I will try and well, I definitely got all the feature fails written for what we've example mapped. I'll start
1:42:50 Conclusion and Future Steps
1:43:04 to hook that up to Cucumber Rust, which is a library. Hopefully, get the features working. And then what we'll do is I'll chase up with you in maybe a week, two weeks time. We'll try and get something else scheduled. I don't know if it'll be December or if it'll be be heading next year. We can work that out later. But we'll try and follow-up and show people how far along we're getting with this and hopefully provide more or at least get more knowledge out of your brain and share it with people because I think it's it's fantastic. Great.
1:43:30 You know, I do do this for a living, so if anyone wants to. Yeah. Yeah. If your team is looking for help in this domain, then maybe best just to, you know, grab you on Twitter, grab you on any of your socials, whatever. James says, thank you. No. Thanks for sticking with us. We hope that was that was useful. So, Karen, I'll leave it back to your day. Thank you again, and I'll speak to you soon. Bye.
Technologies featured
Meet the Cast
Stay ahead in cloud native
Tutorials, deep dives, and curated events. No fluff.
Comments