Ship it! How to do what not to do
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 133 | |
Author | ||
License | CC Attribution - NonCommercial - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/48833 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
00:00
Software developerMeasurementCodeSoftware developerCASE <Informatik>Constraint (mathematics)Code1 (number)Structural loadMilitary baseWebsiteWordGreatest elementProjective planeMultiplication signGoodness of fitSineComputer chessJSONXMLUMLComputer animation
02:08
Software developerCodeBinary fileProduct (business)Complete metric spacePhysical systemWindows LiveFocus (optics)Right angleProgrammer (hardware)MereologyComputer hardwareOffice suitePlastikkarteSelf-organizationTime domainCore dumpEnterprise resource planningCustomer relationship managementDomain nameProcess (computing)Source codeSpreadsheetEndliche ModelltheorieData modelGame controllerEndliche ModelltheorieUniform boundedness principleClient (computing)Software bugOpen sourceInternetworkingLevel (video gaming)Pivot elementWeightData miningGoogolVideo gameSoftwareProduct (business)Moment (mathematics)Degree (graph theory)Point (geometry)Tap (transformer)Arithmetic meanFitness functionScaling (geometry)Insertion lossExpert systemPhysical systemMultiplication signMobile appLine (geometry)MathematicsScripting languageWebsiteRight angleLogicDemosceneProjective planeWindowCircleComputing platformSpreadsheetTelecommunicationBinary codeRule of inferenceConcurrency (computer science)PlastikkarteCodeCASE <Informatik>FreewareMetropolitan area networkOffice suiteMixed realityExecution unitNetwork topologyAreaData managementProcess (computing)SpacetimeElectronic program guideDifferential (mechanical device)Directed graphSoftware developerComputer programmingMetreMicroprocessorLogic gateShared memoryNumberService (economics)MathematicianWordSystem callSatelliteCore dumpEmailDomain nameText editorIntegrated development environmentCross-platformJava appletPoint cloudCentralizer and normalizerBitMereologyGoodness of fitTotal S.A.Structural load
11:40
Software developerPhysical systemProcess modelingIcosahedronBitDifferent (Kate Ryan album)Real numberCodeEndliche ModelltheorieFiber bundleSoftware testingTouchscreenService (economics)Sign (mathematics)Invariant (mathematics)PermutationSubject indexingElectronic program guideFamilyVideo gameGoodness of fitCategory of beingFormal languageFilm editingDatabaseCASE <Informatik>Inheritance (object-oriented programming)QuicksortComputer animationSource code
14:51
Process modelingPhysical systemSoftware developerConvex hullChi-squared distributionArc (geometry)Likelihood functionGame theoryString (computer science)PerimeterSign (mathematics)Traffic reportingEncapsulation (object-oriented programming)Endliche ModelltheorieOrder (biology)Multiplication signSoftwareData storage deviceSound effectMaizeInternetworkingCodeInformation securityInterface (computing)Line (geometry)Domain nameVideo gameGroup actionMoore's lawLevel (video gaming)CASE <Informatik>TouchscreenFunktionspunktmethodeTerm (mathematics)Personal digital assistantCategory of beingNetwork topologySet (mathematics)Physical systemConstructor (object-oriented programming)Primitive (album)Pointer (computer programming)Revision controlRepository (publishing)Functional (mathematics)Object-oriented programmingNeuroinformatikSource codeComputer animation
19:41
Software developerPointer (computer programming)Process modelingPhysical systemVideo projectorAxonometric projectionObject (grammar)WebsiteSocial classEndliche ModelltheorieSoftware bugMappingService (economics)Field (computer science)CASE <Informatik>Type theoryCartesian coordinate systemInstance (computer science)MathematicsComputer configurationMessage passingFreezingDatabaseEncapsulation (object-oriented programming)Latent heatSoftware frameworkProduct (business)Operator (mathematics)Category of beingAddress spaceConstructor (object-oriented programming)Relational databaseRoutingSequelWeb 2.0Process (computing)Semiconductor memoryCausalityLogicDialectRootReflection (mathematics)Software testingString (computer science)Sound effectMedical imagingBlogWeb applicationObject-relational mappingExpandierender GraphStructural loadFluid staticsArithmetic meanIn-Memory-DatenbankDifferent (Kate Ryan album)Operator overloadingCodePattern languageComputer animation
26:47
Axonometric projectionSoftware developerIRIS-TPhysical systemThomas KuhnQuiltProcess modelingMessage passingQuicksortAsynchronous Transfer ModeCross-correlationSoftware frameworkProcess (computing)Electronic program guideRegular graphGreatest elementQuicksortBitLevel (video gaming)Integrated development environmentSolid geometrySelf-organizationString (computer science)Social classImplementationInternetworkingSource codeCodeDifferent (Kate Ryan album)Primitive (album)Endliche ModelltheorieOrder (biology)Differential (mechanical device)Electronic mailing listExecution unitForm (programming)Revision controlObject (grammar)Interface (computing)Substitute goodServer (computing)Video gamePoint (geometry)Likelihood functionWebsiteGoodness of fitUsabilityCAN busSoftware developerThomas BayesCASE <Informatik>Factory (trading post)Principal idealMultiplication signRight angleSinc functionUnit testingWhiteboardPRINCE2SummierbarkeitBlock (periodic table)Logic gateAxiom of choiceTap (transformer)Food energyAreaSource codeComputer animation
33:49
Software developerAsynchronous Transfer ModeProcess modelingComputer networkPhysical systemExecution unitObject (grammar)PRINCE2Installation artCodeInjektivitätAsynchronous Transfer ModeBitSoftwareComputer fontMachine codePRINCE2Interface (computing)DatabaseStatement (computer science)Multiplication signSemiconductor memoryTap (transformer)Line (geometry)Level (video gaming)AreaStructural loadRun time (program lifecycle phase)TouchscreenCoefficient of determinationPlastikkarteMappingKey (cryptography)Instance (computer science)Absolute valueDefault (computer science)Different (Kate Ryan album)Presentation of a groupMedical imagingWebsiteEnterprise architectureGreatest elementMass1 (number)Data storage deviceReal numberSubstitute goodOrder (biology)ReliefLetterpress printingNP-hardQuantum stateNetwork topologyWater vaporVideo gameWeb 2.0Source codeComputer animation
40:12
Software developerPhysical systemSoftware frameworkWindowType theoryContext awarenessMereologyComputer-assisted translationLevel (video gaming)Dressing (medical)Local ringBitService (economics)Total S.A.Revision controlPrisoner's dilemmaInheritance (object-oriented programming)CodeSelf-organizationDatabaseIP addressPhysical systemThumbnailConnectivity (graph theory)Multiplication signSource codeComputer animation
43:48
Software developerService (economics)Order (biology)Software testingRight angleDecision theoryCodeInheritance (object-oriented programming)Revision controlSelf-organizationTask (computing)Interface (computing)Game controllerPattern languageEndliche ModelltheorieGraph coloringSoftware developerLevel (video gaming)Bit rateConfidence intervalComplete metric spaceBitSolid geometryComputer animation
46:53
Software developerInclusion mapSoftware testingService (economics)File Transfer ProtocolObject (grammar)Formal verificationPhysical systemProcess modelingMoment (mathematics)MappingSoftware testingTouch typingUnit testingINTEGRALConfidence intervalMultiplication signBitWritingDifferent (Kate Ryan album)Physical systemPRINCE2ImplementationLatent heatCodeType theoryRepository (publishing)RoutingExecution unitContext awarenessSocial classPoint (geometry)Interface (computing)Line (geometry)Right angleRange (statistics)Control flowWebsiteWordSequelTask (computing)Form (programming)Physical lawExpert systemMachine codeDigital electronicsSemiconductor memoryShooting methodShared memoryFerry CorstenFrustrationEndliche ModelltheorieSystem callComplex (psychology)InjektivitätSource code
52:24
Software developerComputer networkExecution unitIterationData modelReal numberEndliche ModelltheorieSoftwareLogicComplete metric spaceRepository (publishing)Formal languageDegree (graph theory)Different (Kate Ryan album)Data miningBus (computing)Unit testingPhysical systemMassInterface (computing)CodeTraffic reportingProcess (computing)Group actionNetwork topologyIdentifiabilitySoftware testingMultiplication signProjective planeDatabaseBuildingDatabase transactionQuicksortSequelMountain passWebsiteSign (mathematics)Stability theoryReading (process)Source codeSpreadsheetMathematicsService (economics)Goodness of fitWrapper (data mining)Mobile appMoment (mathematics)Point (geometry)Software industryBitCode refactoringRoutingSource code
57:54
IterationData modelSoftware developerCodeAbstractionPhysical systemNatural numberEndliche ModelltheorieSocial classProjective planeCodeDecision theoryAbstractionRight angleIntegrated development environmentSound effectPhysical systemArithmetic meanMathematicsKey (cryptography)Bit rateWordSpeciesPoint (geometry)Lie groupSelf-organizationHydraulic jumpStructural loadData dictionaryMultiplication signRootBoss CorporationLine (geometry)WindowComputer animation
Transcript: English(auto-generated)
00:06
OK, hi. Thanks for coming. My name's Ed. I am a developer. I'm a contractor. So I've been around, and I've seen some very varying code bases. I've seen ultra-electronics, sea,
00:24
as many as sea that's been dropped into the bottom of the ocean with a 99-year lease. And I've seen tin pot websites that last a few months for just marketing, really, for to go with advertising campaigns.
00:40
So what I want to go through here today, really, is about shipping your code. When is it good enough? When is it not good enough? And what to do if you're refactoring from something that you might not be satisfied with yourself before you start. So the first thing I want to say
01:01
is assume that nobody is stupid. And we've all been there. You're on your first day at work, and you're thinking, why did I take this job? Or you're like, oh, my word. This company is supposed to be awesome. And we look at how terrible the code is. Oh, my goodness, all the marketing they do.
01:20
And they're just rubbish. What's this? Or you sit down next to somebody to pair, and you just think, what's this guy's problem? But what I want to say is, just never assume somebody else is stupid, because they're always constraints. You're not always aware of what those constraints are.
01:41
I mean, the obvious ones are time, money. But there are other constraints as well. There's personal things and whatever. And constraints change. So there might not have been any budget or any people working on a project. And then the next week, you're there, and there's loads of money and loads of people. And you can't always judge everybody around you. So we keep the WTFs under our breath when you get home.
02:09
Don't be an arse, basically. And also, even if you're dealing with tiny node microservice, little things that are just
02:22
a few lines of code, they're usually part of a system that is much larger. So do not think about a code being amazing or crap. These things aren't binary, are they? Everyone looks at a system at work, and you think, oh, this is brilliant, or this isn't so good, or we had great fun writing this,
02:41
but oh, my word, hasn't it aged? Just remember that not everything is brilliant, and not everything is crap. So another thing to think about is, when is a product done? Yes, it's not something that we always think about a great deal.
03:03
So you might say, hey, a product is done when all the features are complete. The backlog's empty. It's done. Let's get it on the pub. Never going to change again, is it? Obviously, it doesn't work like that, does it? Because even if you've got a product that has no backlog and no bugs, things change.
03:24
OK, so all your features are complete. So it's done. Go down the pub. Fantastic. Oh, we've run out of money. It's finished now, isn't it? We've all been there at some stage, a project that's running out of money. You've got a project manager or an investor that's saying, you've got till Friday. And then it's done, unless you want to work on it for free.
03:44
What about other things? What if the market changes? I mean, this is famous through the Lean Startup book about pivots. What happens if things change? You explore an idea, and then something pivots. Your product changes, and you have to adapt. What happens if the ecosystem changes?
04:03
WCF, if you're in Windows world, the Communications Foundation stuff, that's been done for years. When was that something that has changed? But oh, we've got call on cross-platform in .NET. And I can't imagine there's lots of things in the Java world and whatnot as well.
04:21
So what if a competitor improves, and you have to unwrap something that's been stuck for ages? I worked in an insurance company, and we had a product that was written in .NET 1.1 and hadn't been touched for four years. And we had to introduce a load of new features. That was fun. OK. So yeah, so Windows Live Writer, has anyone been on GitHub
04:43
and looked at the code for that? It's colorful, isn't it? I mean, there's some good stuff in there, and there's some stuff that makes you think, oh, my word. How do they ship that? But they shipped it, and it's worked until, what, recently, and where they've open sourced it. So what this really all comes down to is that we need to focus on the right areas.
05:04
If you work in an insurance company, for example, and you go, what is that insurance company famous for? What does it do? And you might go around the business, and someone says, oh, we specialize in a line of business space. We're brilliant at insuring satellite launches,
05:21
just because we have some scientists that work for us, and they can really help us manage the risk. So that's great. So why would you go and spend lots of money on an amazing invoicing system? Why, when you can get it off the shelf? So let's think about these core concepts.
05:41
So another example is Transport for London, which is a client of mine. So they're going for a big, big program of work at the moment, and it's been going on for a few years. It's called the Future Ticketing Program. And it's a modernization, so I'm assuming that most people are familiar with Oyster, even if they're not from London,
06:01
from having moved around here in the last few days. And the current system is you have an Oyster card. I'm going to have one more wallet. And you tap it on the gate. And as you tap it on the gate, there's a little microprocessor in the gate that will look at a small amount of data stored on the card, and it will calculate your fare, and it will mutate the card and let you through the gate.
06:25
So the modernization is we don't want that Oyster card. They don't want that technology. What they want now is to centralize the behavior. So it all goes to one unified back office where they can put all their resource into it. And then they can find out more about data,
06:43
and they can manage it, and they can put stuff in the cloud for scale, because you can imagine peak is a lot slower than Sunday lunchtime, for example. So yeah, again, they're doing that by way of a centralized off-ticket office system. It's written in .NET, amongst other things.
07:01
And why are they doing it? Well, actually, why are they doing it? Is it because TFL and the mayor loves .NET, and he loves modern software technology systems? Yeah, that's why they're doing it. Of course it's not. Why? And this is a real principle about whenever we're writing codes, you have to think, why is something happening? What is the underlying reason? Because every time you look at a piece of code,
07:22
you've got a problem in your head. You're staring at your IDE or your editor or whatever. You think, why? What are we trying to accomplish? Not the what, not the how, but the why. Because if you understand the why, you've got that deep meaning of what you're actually, how you're going to achieve that. The what and the how are artifacts of the why,
07:40
and it's something that we need to know. So for TFL, they want loads more people going through it, and they want to reduce the total cost of ownership of the system. Every journey matters is their cheesy tagline, and every company has one. Sometimes there's a meaning behind it. Sometimes it's just marketing dribble, isn't it?
08:05
So what do we focus on? We want to focus on our core domain. You know, for Transport for London, that is piecing taps into journeys so you can charge people the right amount. If you're an insurance company, like the space people, it's actually about number crunching all the data
08:23
and the telemetry, and mathematicians being able to do that. And it's not about how wonderful their website looks. Another example would be accounting. So I have an accountant, and their website, you know, I hope there's no one here from there. It's awful. It looks terrible.
08:40
It's written in PHP, which makes, I'm a snob and it makes me go. And the JavaScript on it is awful. It's laggy. But I don't care, because I can pick up the phone and talk to them, and they can change anything behind the scene because they use a giant spreadsheet, Google Docs isn't behind the scenes, I think. Whereas some of their competitors have wonderful, sprangly websites.
09:03
And, you know, but if something goes wrong, someone pays you late, or they forget to pay the VAT, it's very difficult to deal with. So the core domain of the accountant is their back office system that consumers never see.
09:20
So other things are subdomains. So this is probably why we all see SharePoint everywhere, and everyone hates it. It is the most god-awful thing. And if you're lucky, you might have a competitor there at a place. But why do people have it? Because they don't have to think about it, because it's not their core domain, you know?
09:40
Why would you, if you're a manager? So, we know what we want to focus on. We want to focus on the core domain. That's usually the differentiator, yeah? So that's what is proprietary, what's amazing about your business, what is the idea of the startup or where all the investment's gone
10:01
from a large company, where the market's heading. So, you've got the idea, now you've got to turn it into code. So, what we've got to think about here is, how do you put that into your code, that idea? So, what, is it CRUD, you know?
10:23
Is anyone here familiar with 38 Degrees, the website in the UK that does all the petitions and things, yeah, that span the hell out of everybody all the time? You might have signed up for bees, you might think you're either a left or right wing activist for the amount of emails you get. Right, so what's so special about 38 Degrees?
10:41
Do they operate at scale, they've got hardly any IT cost? Why? Because it's CRUD, because behind the scenes, all they're doing is they're hitting Google Docs. Every time you fill in a form, it just fills it in on a spreadsheet behind the scenes and they let Google deal with all the concurrency. They don't have an army of software developers and they don't have an amazing node MVC
11:02
or whatever towards your kind of app. They don't have any behavior, it's all about collecting data and sending emails. That's it, they don't need anything more complicated. Now, if you're working on something a little bit more complicated,
11:21
this is probably what you want. You want a model, you want to put all your business logic into something intuitive. Oh, excuse me. Wait for my VM to load. So what I'm going to show you now, if the VMware wakes up, is some code.
11:43
Now that is a little bit larger than I was expecting. So I do apologize, I should have checked this scene. It's this one, isn't it? No, that's wrong. Well, I'll do it this way if you don't mind.
12:03
Sorry, guys. There we go, it's a little bit better. So using the very simple TFL example from earlier, I'm going to show you a very simple model. This is, for brevity I've cut 90% of it out.
12:21
But the general thing here is we have a journey and from that journey we want to generate a fare. What do you charge someone at the end of the day? So I would say that what I'm going to show you on the screen here in this example is what you'd call the simplest thing that will work. Yeah, I think some of these people in the room will look at this and go, God, that's awful code.
12:42
I can see lots of smells with it and it's not going to win any awards, right? Let's have a look at it. So we've got a repository, a service, and a model. And it's very anemic.
13:01
So what's wrong with that? Well, if it works, there's nothing wrong with it, is it? If that works, you could ship it, couldn't you? I believe that someone's doing a property-based testing talk today or tomorrow, certainly this week,
13:21
and whereby without wanting to butcher what property-based testing is, there's lots of different permutations go through the model or through what you're testing. In this case, it's going to be our journey model. And look at these primitives, things can go wrong. What happens if the origin is empty or just a random string, if it doesn't match anything?
13:42
Or you could go to, oh, I've got a GUID here and what happens if my indexing in a database? There's so many things that can go wrong with that. And if we go down, where's our behavior? Oh yeah, our behavior is here. It's in a sign fair in a service.
14:04
So we've got a model which has nothing in it but data and we have our behavior in a service. And this is an object-oriented language, so you could say, well, shouldn't they be together? Well, if it's as trivial as that, why bother, frankly?
14:23
But that's going to be very, very buggy because there's the, there is no way of testing, well, there's no, none of the invariants have been enforced. So you can send any old rubbish through that.
14:41
And if this is coming from a real device, real piece of hardware, then you are in trouble, aren't you, frankly? So let's have a look at a slightly more, slightly refactored version. So here, we're going for a fat model. So what have we done here? Well, we've slightly refactored it.
15:01
So we're now passing things through the constructor so we can reduce the amount of mutation once it's in there. And we've now moved a sign fair into our model. And I'm using a func here instead of the actual interface.
15:27
So I can test it easier. And also, at this stage, I want to keep things as simple as possible. I don't care how I do it. I don't care how that fair is generated and assigned. All I care is that it gets it out.
15:44
So we've got a string, a string, and a short. So what I'm saying is I want an origin, a destination, and a short. And a short represents a decimal, or not a decimal, rather, a sum, like one pound. 105 pennies, whatever. So, you know, I'm completely ignorant.
16:02
And here's the repository. And all I'm doing is calling it here. Nice and simple, yeah. So what have I gained from this? Well, I've moved things around. I've actually got more lines of code, but I'm stepping towards something really simple, which is that I'm restricting where things can go wrong. If you look at a security breach,
16:21
you see on the internet, oh, such and such has gone wrong. Often, how do people reduce their problems? They reduce their footprint, don't they? Their security footprint. And this is what we're doing here. We're reducing the likelihood of something going wrong, because we've got rid of the mutators, so there's no setters anymore, and everything's going through the constructor. And what we really ought to be doing here
16:42
is enforcing these. So something like...
17:05
Yeah. I won't bore you all with live coding, but that's the gist of how we would start to enforce these things with these primitives. So that's the fat model.
17:20
I would argue that, again, it's shippable. But, again, what happens if the behavior starts to get more complicated? So far, we've been talking about this funk down here, a signed fare. That's where the behavior really is, isn't it? How do we get that fare?
17:42
Well, I can tell you very excitingly what TFL do is they spend three days simply generating all the primitives fares across the entire network, and it takes a ridiculous amount of computation power, and once they've generated, they're all cached, and you're just querying them from a system. So what's next?
18:02
So here, I've gone for something that is often called tell, don't ask. So what we're doing here is we want to tell our journey, the fare. We don't want to ask anything, what the fare is. We don't want to risk exposing things because what happens if things change, right? So if you're refactoring something,
18:23
so you go into somewhere, and someone's got this terrible piece of code, and you don't like it, and you want to, and one of the reasons you don't like it is because all the properties are being jigged around, people are moving data outside of your model. You set something, somebody else sets it over there. So how do you do that? Scope, encapsulation.
18:41
It's the most basic thing in object orientation. So yeah, here we go. So we're setting those, and now we can't see them. And we're setting our fare internally. So what's the fares? Okay, so this is just a simple delegate because we're dealing in terms of domain here. So this is a domain.
19:01
I don't want to deal with things like strings, and integers, and a func. What a func of, what was it? String, string, short. What does that actually mean to anybody? It means nothing. If you see that on the screen, what does it mean? So here I've got a delegate, and it explains what it is. A simple function pointer that is explaining to me
19:21
what it's doing because it has the name in it. Okay, I could refactor that. You could say, well, that still doesn't make sense to me because I'm not familiar with the domain. Okay, we can change that to, ooh. You know, yeah, ooh.
19:42
Oh, that was clever, sorry. Yeah, that might make sense to some people. Okay, so again, can we ship that?
20:03
Yes, of course we can, but what we're really getting to now is, well, if we can ship anything, why are we continuing to refactor? Well, that really comes down to your business, isn't it? So at the very beginning, we were talking about why. If you really understand why you're doing something,
20:21
you understand what you can get away with. So what was it that TFL wanted? They want high throughput, and they want cheap fares. So when they say cheap fares, they mean the right fare, yeah? So what they really want is they want no bugs, no side effects, because if they charge people the wrong amount, it's very expensive for them
20:41
to then, you know, to go through a process of reimbursing people, paying someone to manually calculate what the cost of the route should have been if it was expensive. So actually, if you were somewhere like TFL, or arguably anywhere, if I was at the insurance company I was discussing earlier, I wouldn't be able to ship that code, you know?
21:01
There's far too many things that could go wrong with it. If I was working for an ad agency, and they wanted a simple web app that's collecting names and addresses for a marketing campaign, yeah, go for it, you know? That's fine, you know? It's the why, isn't it? The why determines the what and the how.
21:22
So one thing you might say is, so I like my properties, I like my getters and setters, I like my getters particularly, how else do I get things out? So this is a simple hack, yeah? So imagine that you're looking at a piece of code
21:42
at work on next Monday, and you're like, well, I've made everything private, how the hell am I gonna get it out again? What's all this? Well, if you're using nHibernate, Entity Framework, Hibernate, if you're using those kind of things, those ORMs, they provide access to your private fields.
22:01
Now you might say, but I'll use my type strength, because I'm using magic strings, or I'm having to use conventions, and if things change, I don't like it. I would say that you're more likely to have a bug or a problem, because you've exposed some data which has been misused in the business logic,
22:22
than you are because you're missing a column in a relational database. If you're missing a column, if you're trying to say something, you're missing a piece of data, things will break very quickly. You should really be testing for those kind of things. You can test an in-memory database very, very trivially now. Most of the ORMs allow you to use things like SQLite,
22:43
and SQLite has an in-memory database option, so just use that. In fact, I know fluent-n-hibernate has a specification test class in it, so you can actually test your mappings
23:02
to make sure they're correct. So if you set up your fluent-n-hibernate to actually say, I know it can have always wonderful static reflection in that lambda, it's always the same with entity framework, but I'm using strings, what? Or I'm using a convention where I say I just want all the fields internally,
23:21
then just do that, you know? It doesn't matter, you know? But if you're looking at me going, what the hell is this guy on about? No way would I ever give that up. Then, you know, there are ways and means. So this is a simple example where I'm just literally spitting out an anonymous object.
23:40
You could use an expander object. There's lots of different ways. You could create another type. So you could be saying, well, what on earth is all this nonsense about? So why would I return these when I could simply just return the actual type again?
24:05
So you can. Here's a journey memento. Don't know if anyone's familiar with the memento pattern. That's not what this is. A memento in this instance is simply the data. So this is a really good hack for if you want,
24:23
if you've got loads of anemic design, loads and loads of anemic objects, loads of DTOs everywhere, and you can't put all the behavior into them because there's too many usages. You're in ReSharper or IntelliJ or whatever, and you look at usages and there's hundreds. You're like, oh my god, I'm never gonna be able to change that because I mean, we've all done it.
24:41
I just want to meet that class or I just want to meet that method off that service into that model. I'll just move it. 600 usages, phew, forget that. So this is a kind of a long-tail way of getting around that whereby you create your new, spangly, shiny model and you pass your horrible, crappy DTO
25:03
into the constructor. Now this comes fraught with lots of issues in itself, but as you can see, we just return it out if you need it elsewhere. You could even go as far as simply having an implicit or an explicit static operator overload
25:22
to convert it back. So they could implicitly just swap around. But you do come into problems with that. So let me see if I can give you an example here. So here's a simple test.
25:41
Okay, so here's a nice and simple test. So here's our memento, we pass it in, and we get what we want out. So I want to demonstrate this one, which is tinkering, okay? So what was I talking about earlier on
26:00
about scope and encapsulation? So if you've got a DTO over here and you've passed it into something over here, what happens if you change this one over here? Exactly, it changes this one over here. So you know, you have to be very, very careful so if you do take this approach,
26:23
I've found, so I've done this for real in a very, very large application is benefit, it gives you, it's fast. You can do this very, very quickly. The disadvantage it gives you is you just mess yourself up with reference, haven't you? So yeah, would you ship that?
26:42
Yeah, I've shipped it. In fact, if you came here using contactless with TFL, that's pretty much how it all works. How do you mitigate for it? Lots and lots and lots of unit testing, essentially. So here's another step you might want to take
27:01
to try and improve your model, where your magic source goes, the differentiator between my implementation and someone else's implementation of something. What's gonna make me the best on the block? So everyone's familiar with solid, right?
27:22
We all go through it. If you've ever interviewed somebody or gone for an interview, you learn it by heart. It's like, right, it's almost like, you can't go for a job interview almost without somebody asking you about solid. But I would say, take a step back from solid and look at even simpler things. Instead of thinking about, oh, how I got interface segregation principle
27:41
or how am I obeying Liskov substitution principle, think about what you're actually trying to achieve. So a problem, an anti-pattern I often see is somebody goes, this journey here, someone will put an interface on it, iJourney. Great, I can, what, stub a journey? The journey here is an entity.
28:01
It's representing behavior and data of the entire thing. If I put an iJourney interface on that, I would gain absolutely nothing from it. It would be a very pointless thing to do and would probably cause more confusion because then it would encourage anti-patterns like poor mocking where someone ends up mocking an entire journey,
28:21
but the behavior's in the journey, so you're not actually testing the real thing. So you've just completely missed the point. So, you know, I would suggest that if you're gonna use interfaces, use them for discrete little things, the roles, iQuatable, iComparable.
28:40
What do these do? These are baked into the framework in this example, and what it's doing is it's allowing me to say, I can compare two journeys by ID. I don't necessarily have to expose the ID. I might choose to, but I don't have to. And if I'm comparing for sorting things, then they'll sort in the correct order. But hang about a minute. If I'm gonna sort something that's done by GUID,
29:02
that's gonna be an absolute mess because how is a GUID ever sequential? Well, that's not good, is it? Okay, so all we've got here is a sequential GUID.
29:23
Nice and simple. So, I don't know if anyone's seen this before. It's, there's lots of different ways. If you don't want to use something out of the kernel, perfectly reasonable, then go to the nHibernate and look at the combined GUID where they've actually broken up the bit array,
29:41
the byte array, rather. And all we're doing here is we're using the same thing that SQL Server uses to get an actual GUID that's sequential, which then means when we go back to our journey, we can have a list of journeys and we can order them. And we don't have to expose things we don't want to. They can still remain internal.
30:04
So, another thing here that we don't like is all the primitives. Primitives are easy to make things wrong and they're not intuitive. You don't know what something is. Remember that delegate earlier, the func? It's got string, string, short. That's not helpful at all.
30:20
There's a, you can download value objects as an example of one here on the internet. But personally, I tend to just do them myself now and just implement IQuatable. You can use ReSharper very easily to create these and I'm sure, well, I know IntelliJ's the same. So, instead of passing in a string for an origin, a string for destination,
30:41
we've got a simple class to do that for us. And we can compare two origin and destinations, nice and easy. And here's an example down the bottom here of, ooh, where was it? Oh, sorry. Of an account ID to wrap a GUID.
31:00
Because how many people here have passed the wrong GUID into a method? You know, there's nothing worse, is there, looking at a method and it's got four GUIDs on it. You're like, which one do I use? Make life easy. Use value objects instead. Okay. So, you're going along, you're shipping,
31:22
you've shipped all these versions. And now someone says, I need, excuse me, I need to go asynchronous and I need to go distributed. So, we start looking at a more messaging-oriented approach. So, here, what we're doing is we are
31:43
now receiving the tap. So, the tap comes in in the form of a command, okay? So, here's our command. Nice and simple, that's the data. So, if we go back to our anemic model at the very beginning,
32:02
there's not a great deal of difference there, is there? So, if you're an organization that doesn't do regular deployments, you can't even get to a regular staging environment. If you're at one of those crappy places in the city, a bank or whatever that just, oh, the best way to mitigate for things going wrong
32:21
is don't deploy, which is generally what these kind of places do. Then maybe you might wanna just bypass straight to the end, which I would never recommend ordinarily because you want your design to emerge naturally as you go along. So, if we have a look at this, all we've got here is the ability to receive taps.
32:48
We order them, we sort them. So, we add them and then we sort them and then we can get the first and the last tap to determine the origin and the destination in the journey. Okay, so hopefully now we're all bored
33:01
of understanding a very simple model. But what I wanted to emphasize there is there are many, many different points where you can stop along the way and go, can I ship this? Don't just refactor, refactor, refactor, stop. When can I ship it?
33:21
Only you know, you as a developer, your team and your organization. If you deploy regularly, then you could've gone through each one of those stages and in that way, you are completely reducing the likelihood of something going wrong. So, this is all very good, but what happens
33:41
when we've got some really, really nasty code that we wanna try and hide away? So, what I wanna do here is show some pretty common examples of little ways around we can have some crappy code. So, he's seen code like this. I call it spaghetti code, if else everywhere.
34:00
And hey, again, can you ship that? And probably if you've only got two values, it's not the prettiest thing in the world, but you know, that's intuitive. That's a big font if it's on one screen. If you can't understand that, you know, what are you doing? But obviously, things get a bit more complicated.
34:20
Not all values are hard coded. So, we step it up a little bit and now someone says, I'll use a case statement. I'm gonna have switches. Nice and simple. I can have loads more of these and we've all been there when you've got switch statements, 50 lines long. What happens when they start to get nested? It gets a bit more complicated then, doesn't it?
34:42
Maybe I shouldn't have done that switch statement. Here's a handy little way of trying to mitigate that. So, this is a, I think it comes from Python. Which is use a dictionary. It's nice and simple. And all we're doing here is we're taking our key
35:00
and mapping it over to the behavior. So, okay, that's a 10. That's nice and simple. But that could be anything. Behind that, you could have the most monstrous piece of crappy legacy code that nobody wants to look at in their life. And, if you wanted to, you could inject it.
35:21
You could pass it in. It's a dictionary. You can add or remove these items at run time. So, you don't need to worry about, oh no, how do I change my switch statement as I go along? What happens if I want to introduce something or remove something? You don't have to with that. And that's a good get out of jail card because how many times have you been somewhere
35:42
and you thought, I need dependency injection. But the code base is such a mess. It's just not feasible. What happens if you want to use a, introduce a container and it's just too messy? So, hopefully this is a much more familiar way of doing this for people.
36:02
So, here we are. We're injecting our network. Our network here being a method of getting fares. So, what I've also done here is I've introduced another interface and that interface is,
36:21
somewhere else, there we go, is network, which is the get fare. And I've also introduced the interface for, I satisfy, I can satisfy, the satisfy mode. So, what is that doing? That's pretty simple, really. It's just a predicate and if the mode matches,
36:42
it returns true. So, and what I'm enabling here is the ability to hide some really, really disgusting code because I can, behind that iNetwork interface, I can have the absolute dog's dinner code and it's nicely hidden behind these.
37:04
So, I can now inject my array of networks and no problem. What I do, I just iterate over them until I find one that matches and then I can simply invoke it. So, what I've got here is 101, isn't it, really?
37:22
How many times do you have an if statement and you forget about what happens if nothing matches the if statement? What about the default on a case statement? And it's the same with here. So, I've created an invalid one and so I find that that is a reasonable way of getting around that problem. So, let's have a look at some specs here.
37:45
Okay. So, what I've done here is using that prior example is I've introduced the container. I've used Windsor here, but you can use a container as a container, isn't it, you know? So, what I'm stepping through here
38:00
is if we start at the bottom, make more sense. So, I'm asserting that we're going from bank to Prince region. I hate you all went to Prince region. Otherwise, you had a bit of a walk, didn't you? They never say that on their website, do they? Go to Excel, whatever, custom house and yeah, no thanks. It's a bit of a walk, isn't it?
38:20
Unless you like Costa Coffee halfway along, whatever it is. And we are handling the taps that come in and yeah, so here's the setup. So, here we are creating the commands, which I showed you earlier. And this is, we're going by rail.
38:43
So, the first one here is a tap at bank, it's rail. The second one, which has just gone off the screen a little bit, is Prince region, it's just a tap out here. So, obviously we need to store things in an account. So, I have an iAccount and here I have a substitute, which is my account spy, which is going to stop me
39:04
from needing an actual, real database. So, I can do this in memory. And I'm substituting that out for that instance and I'm asserting that they actually happen. So, here, what we've managed to do here
39:21
is we've gone from this, absolutely in basic anemic journey and we step right the way through to a stereotypical, enterprisey, dependency injected setup.
39:42
And so, what we've done is we've given ourselves the ability to simply decide when are we gonna ship. Each one of those areas is a stage where you can go through on your journey to actually getting to, you're done. Although, as we've discussed, you're never done, are you?
40:02
So, I think that's a good example of where to shoehorn behavior. Now, if we're interested in how do we actually manage some of this, what I'd like to take you through here is a little bit on feature switching. So, I hope people are vaguely familiar
40:20
with feature switching. So, what happens if you're an organization that has release windows every month, every week, or whatever, and you can't actually get to ship when you want to. So, you're writing your code and you're really happy and someone says, it has to be done by Friday, so you're gonna miss the window. Well, how about you put it in, incomplete,
40:42
but you switch it off? Okay, so everyone's familiar with that, I hope. It's toggling. Or what about if you just don't wanna put it on yet because it needs a little bit more thought, the business aren't comfortable yet, or it's part of a campaign that's not gone live. So, here's a simple one. We're gonna toggle something on and off.
41:02
So, here's a feature. And what I'm doing here is, again, I'm using a container trick, but you don't have to use a container. And what I'm saying here is I want my toggle to decide whether something is on or off. And I've got a toggle type here.
41:20
And if we go to here, I'm saying I always want on, okay? So, my feature map, which takes the type that I want to resolve and the type that I'm actually going to invoke, to implement the component and the service, what I'm saying is for our feature,
41:40
it's always gonna be on for this particular feature. Now, what you could do here is you could inject some kind of database into here or whatever that decides some global thing, what does or what doesn't. So, this can get a little bit more complicated. So, let's have a look at, say, some behavior.
42:05
So, I've got some experimental code here. And I want to decide whether, again, here's the test, and I'm just using a simple map. I want to decide whether I'm using this or this.
42:24
So, what I'm doing here, I'm simply injecting the HP context, and I'm saying, if you're on that IP address, I want to give you the experimental version, so you can then deploy that. So, I was doing some work for Tesco,
42:42
and they always have all these crazy ideas. So, what do we do? Prepare everything behind the feature switch. They always came from the same IP address. If they're logged onto the system from that IP address, they always fancy new things. They can look at it. They can decide whether they like it. And they could give it the thumbs up or the thumbs down.
43:02
What about, yeah, so that was IP, or time. So, like I said here. So, we've got, how about, nice and simple. Have you passed a certain date? Yeah, have you passed a date? Great, have the new feature.
43:21
We haven't, it's the old feature. So, again, we've put a very thin layer on top of hiding some really, really, what could be fantastic or awful code. And why is this? Because everything here is based on composition. Everything is explicitly created.
43:47
So, if we want to,
44:03
so if we want to think about what our bad and our good code is, what is it? One of the things that we really want is we want composition, yeah? So, what's composition? Has everyone ever heard of
44:20
favor composition over inheritance? Well, I'll say this. If you want to use your clean code, it doesn't matter about solid or any of that. You want to new things up and not use inheritance to use code reuse, yeah? It's a big anti-pattern. I'd fairly recommend that you don't use it.
44:41
So, therefore, I would, in all these examples that I'm here, they're all on GitHub, they're all trivial. I'm hoping that everyone can just simply understand it without actually needing to look at it later. But because everything starts from a composition root,
45:00
we can decide what we create, how we create it, and when we create it. So, what's that got to do with shipping code and bad code? Well, it means that we're in complete control. We have absolute control over our own destiny because here we are able to decide
45:20
what version of the code, when we deploy it, and who sees it. Is anyone familiar with A-B testing, split testing? You could use the same thing for that. So, you could say, I want 50% of the people to see this and 50% of the people to see that. And this is all because we are following
45:41
some really, really simple stuff. Now, I'm hoping everyone's looking at this again. This code is naughty and that's deliberate. It is naughty. It is really simple. Because if we are using really explicit code and really simple ideas, then anybody can understand it. And by using something as simple as a simple interface,
46:02
you can wrap up the most ugly code, can't you? So, what you've got to decide is how do I refactor from that anemic design to something a little bit more meaty? Does the behavior live on the model or does it live in a service? And how do you get from that stage to the next? And when can I ship?
46:21
And those decisions really come down to your organization because I'd like to think that you as developers would deliver as often as you can. So, at every stage of each one of those examples, you'd say, yes, I could ship that. So, what's the big thing we talk about
46:42
when we talk about having confidence to ship? And that's testing, isn't it? So, let's have a look at a simple three, well, some simple tests, right? Testing styles. So, here's a familiar, I hope, to everyone, testing style, right?
47:00
So, this is, you know, well, I don't need to explain that, look at it. I mean, it's AAA and that's that. So, what's good about that? Simple, what's bad about it? Well, you know, it is testing that, but if you've got lots and lots of units that you're trying to test,
47:20
that's a very expensive way of testing things. So, I would like to say, think about appropriate testing. So, I would say this kind of thing gets expensive to test in this particular way. And here's an example where I've got a SQL thing, pretend this is SQL.
47:40
And what we're saying here is, you know, I don't want to test my SQL. How do I test it? What's the difference between a unit test and an integration test? An in-memory test and a test that touches IO? I'm not gonna answer that, you all know. But sometimes, I think, dogmatically,
48:00
we all pour to write these kind of tests because it's the way we should write them. And if you look in Kent Burke's book, a lot of his examples are similar to this. But there are other ways as well. So, here's kind of more of the same, but you might say, hey, I can mock some of this stuff. So, here I'm gonna mock my repository
48:21
so I don't have my SQL problem. But I would say, you're still doing the same thing. You're still writing lots and lots of tests. Hey, tests are good, coverage is good. But they get very expensive, and these tests are brittle because what are we actually testing here? I would say we're testing the implementation and not the behavior. What do we really want to be testing? We really want to be testing how the fair works.
48:42
And we want to ship fast, right? So, if you've got to write hundreds and hundreds of those kind of tests, it's gonna take you a long time. If you've got a lot of people, that's fine, you know? But if you want to cut corners, if you want to work at a startup, I mean, I'm sure a few people here work at fast places.
49:01
I find that writing tests like that becomes expensive. I also find that I prefer to write a context specification style. So, what's the difference between this and the previous tests? Well, if you're testing something very simple like this, not a lot. Probably looks a little bit more verbose, to be honest.
49:21
But what we're actually doing here is we're being super explicit. So, I remember a moment ago, I mentioned all about, I said we want to be explicit. Explicit code is simple code, it's intuitive code. People understand it. Here, it doesn't matter whether you call it a range, act, assert, or given, when, then. The point is is that we are able to,
49:43
we are being very, very explicit about what we're actually doing and why. So, if we go down to here, so here's me going, don't write unit tests. Well, what is a unit test? Well, Ken Beck says that a unit is something that shares states.
50:03
If you look at the seams, between your seams, you're sharing state, and that is a unit. And what I would like to say is don't worry about what is a unit test and what is an integration test. Put it in your head and throw it away. Controversial, maybe?
50:20
Don't think about what type of test it is. Think about the behavior you're trying to test. Why do you write tests? You write tests because they guide design if you're writing them first, which I hope is something that a lot of people do. But also, you want the confidence. And you want confidence, you want it to run quickly, and you want them to be maintainable and understandable.
50:40
If they break, you want to be able to fix it. So what I've got here is a more convoluted test. So you might say, oh, my word, there's a lot more lines of code to this test. But what I'm actually testing here is from what I'd call a composite route. So if we're gonna compose everything, there's a route.
51:02
There's one thing, and everything else comes from it. If everything's composed, there's one place where it starts, and that's our system under test, that's it. So in this example, that's this. This is our handle device. So our handle device is the thing that takes a tap and turns it into a journey and all that stuff.
51:20
So what am I doing here? If we work backwards again, because it's hopefully easier to understand from these assertions, is yes, we're testing that you go from bank to Prince Regent, and that the fare is five. But what we've managed to do here is we're testing the behavior.
51:42
So if you look at the cert, it's the handle. That's the only thing that's being tested here. What happens when I pass that tap in? Everything else is just set up. So because I'm using the container, all this is is I'm just swapping things around.
52:02
So by default, I'm saying just get, wire all my things up automatically. I'm not bothering to do any kind of magic, kind of explicit mapping interfaces to classes. In fact, how many people put an interface
52:20
on a class just for dependency injection? Yeah, does it bother you? You think, why am I doing this? Because I can swap it around? You don't always need to. If we look at that, if we look at the handle,
52:42
sorry, I had a reshuffle problem earlier, and now I'm really regretting I didn't sort it out before this. So if we look at this, there's no interface. And that is because it's always the same. All it does is delegate down to the other things. So remember the networks earlier on that we were talking about, which is our wrappers around all our crappy code?
53:03
So what we're doing here is simply loading our journey, invoking our behavior, and then storing it. Anyone else notice that we've been explicit about our naming as well?
53:21
Is that a journey repository or an account? If you find out for TFL because you've got a problem, they ask you for your account ID, don't they, or whatever, or a unique identifier. They don't say, oh, do you know what journey repository you used? This is a domain, it's a model. It represents something that happens to real.
53:41
If you work in the software industry to do with animals or vets, are they called horse repositories? No, they're stables, yeah? Think about the naming as well. So I've not bothered putting on an interface on this because I'm, in my test, I'm always going to resolve the same thing.
54:05
It never changes, okay. So the only thing that I'm actually substituting here in this example is the account.
54:22
And I'm changing it for my spy so I can actually unit test and put the assertion on it. And this is just simply a dictionary instead of an actual SQL database. So this test is testing everything that all the other tests have done.
54:40
So it's bigger. It's a little bit more complicated. But if it breaks, there's one place you can look and it's the behavior. So what should break that test? Well, something to do with the fare. It's the only thing that should break that test. What happens if I want to go on a wild refactoring thing?
55:01
What happens if I get new motor transport? What happens if, you know, there's a mayoral election at the moment and one of the mayors has just turned around and said, oh, I want to make it so that when you get on a bus, your ticket is valid for an hour on any bus journey. Well, that completely changes a lot of business logic. But what changes in the test?
55:22
Nothing. The only thing you'd probably want to do is just put an assertion to make sure that it actually happens, yeah? And why is that? Because we are testing what happens when we bring our tap in and it comes through our composite route here.
55:42
So, go here. So what are we trying to do? We want to understand why we're doing something. If you understand the why, everything else is a natural artifact. You saw, if you start somewhere or if you're on a project or you start a new job
56:03
and someone says, we're building an MVC app using AngularJS and, yeah, and we're gonna use all these great things. What? What's the problem? What are you trying to solve? What's the point of the software even existing?
56:20
You know, if you're sat there and you've got a problem, you think, how am I gonna solve this? If you understand the why, why am I even doing this? Why does someone pay me to sit at this desk? Then you're far more likely to understand how to solve a problem. So another thing, obviously, is you want to appreciate, when is something good enough?
56:40
When is it good enough to ship? Well, it depends. So, remember the 38 degrees thing? When was that good enough to ship? I'd imagine they ship very early, you know? I mean, what could go wrong? It's a spreadsheet. It's a glorified spreadsheet, you know? When did they ship? Very, very early, I suspect, right? If you're building a distributed system
57:03
across the whole of London that deals with millions and millions of transactions, guess what? Ships doesn't ship anywhere near as regularly, yeah? And we want to put the behavior in the model, okay? Every time you look at a piece of software and it's got complicated logic
57:22
and you think, okay, I've got an interface here that's a service and I've got an anemic domain, soon as you have to change something that will introduce complicated logic, you're in trouble, yeah? So try and put everything in the model, all the business logic, all the special source, the stuff that makes your business
57:40
different from someone else's business, makes it worth doing, okay? Because things emerge, yeah? The reason why I would recommend that you go through each one of those steps, skip the memento one, is because you want the problem to emerge as you go through it naturally, okay?
58:00
Because one of the things that you're obviously trying to do is you're trying to avoid entropy. So if you put everything in the model, it's more intuitive, right? What is intuition as a dictionary definition, right? You want it so that someone can just implicitly understand something straight away, yeah? If someone can understand something,
58:22
you don't need to sit down and go for a 40-page word document. When was the last time you sat down at work and your boss said, please write me a word document about how the system works because we've got a new starter? It's embarrassing, isn't it, when that happens? Because if your code is intuitive, then your new starters won't need that much handholding.
58:44
Okay, so I've already covered composition, I hope. So you don't want to be using abstract classes for things that aren't about abstraction. You know, the giveaway's in the keyword. Abstract abstraction, not key, not word, not reuse, okay?
59:05
So that's it, really. I mean, has anybody got any questions? No? Cool. So the key points I'm trying to drill in here are use a model, put your behavior in a model.
59:24
Composition, have a composite root. That's a phrase you can Google. And try and keep things simple. Be explicit, always be explicit. Just be as explicit as you can. It's funny how when we speak,
59:41
we try to be clever and use nuances and hidden meanings behind things because we think it makes us look clever. We tend to do that in our code as well and it might make us feel good about ourselves but it's not what we ought to be doing, you know? Question.
01:00:03
Yes, yes, yeah, of course it does. So I mean, I would say that's more
01:00:20
of an organizational challenge. So the way that you would combat that is twofold. One is the why, if you understand the why, you can understand how far you can get away with something. So if you know that there's a delivery window coming next month and you're not gonna get another one for six months, chances are out of those steps
01:00:40
that I showed, these steps here, you might want to try and skip anemic and don't bother doing it at all and go straight for this one, for example, yeah? So if I'm starting a project, because I'm familiar with this stuff and I'd like to think some of you guys are as well,
01:01:01
I generally go straight for that. That's how I write my code. Okay, this is very, for brevity I've missed a lot out. But so to help manage tech debt, don't do stupid things by using anemic models straight from the start.
01:01:21
If your organization does very, very infrequent deployments then you're in a situation there whereby you need to think about, do you take incremental steps or do you go for the big bang? And I would still personally always take those incremental steps because things change,
01:01:42
environments change and otherwise if you don't, you could be the person, like the very beginning that everyone's calling a useless idiot, forgetting it wrong and making these big grand design decisions and missing the deadline or something going wrong. Because these things should emerge as you work on them
01:02:00
and if you make too big a jump, then what happens is the pivot and you've made loads of assumptions, you're in trouble. So incremental change is basically what I'm saying. Is there any other questions?
01:02:21
No? Cool, all right, well thank you very much everybody.