We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Beyond the compiler

00:00

Formal Metadata

Title
Beyond the compiler
Subtitle
going up to 11 with conventions in a statically typed language
Title of Series
Number of Parts
110
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
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
C# (or Java) developers looking to cut down amount of repetitive boilerplate code but wary to let go of safe harbour of compile time checking Approach of incorporating conventions to cut down on repetitive boilerplate code has been around for several years. How can we apply this approach in a staticaly typed language, like C#, to best leverage its strenghts while retaining benefits of the language and .NET plaftorm? This talk will push the boundaries of your knowledge about using conventions. You will learn how to properly apply the aproach to dramatically cut down on the code no-one wants to write, and how to build application specific "compiler" to validate your conventions. And have fun along the way.
CompilerFormal languageFrictionSoftware developerFrictionComputer configurationProcess (computing)Open sourceSoftwareSystem callGaussian eliminationProjective planeSlide ruleOptical disc driveSet (mathematics)Coefficient of determinationInformation securityBlogMereologyPhysical lawArithmetic meanSoftware developerInformation technology consultingCodeBuffer overflowTwitterTask (computing)Different (Kate Ryan album)Client (computing)Stack (abstract data type)Demo (music)Computer animation
Software developerPresentation of a groupRight anglePhysical systemTwitterTheory of relativityUniverse (mathematics)Sign (mathematics)Similarity (geometry)WordSoftware developerHash functionCode
CompilerJava appletSoftware developerVideo game consoleComputer programSymbol tableCompilerAerodynamicsError messageGroup actionRing (mathematics)Streaming mediaVolumenvisualisierungFormal languageMultiplication signSoftware developerCompilerC sharpCharacteristic polynomialMereologyMathematicsCategory of beingCompilerComplete metric spaceReal-time operating systemRun time (program lifecycle phase)CryptographyType theoryRight angleSocial classView (database)Natural numberArithmetic meanSet (mathematics)1 (number)Group actionInstance (computer science)TwitterWordBlogCellular automatonTraffic reportingRevision controlClient (computing)Video gameHierarchyCorrespondence (mathematics)CodePhysical systemWeightQuicksortComputer programmingEvent horizonSingle-precision floating-point formatAlgorithmFigurate numberSystem callComputer virusOnline helpRule of inferenceInsertion lossGeneric programmingMoment (mathematics)VideoconferencingChief information officerError messageLie groupMetadataMobile appVarianceMappingGame controllerShift operatorDemo (music)Validity (statistics)AbstractionElectronic signatureGoodness of fitInstallable File SystemComputer animation
Streaming mediaVolumenvisualisierungSoftware developerCodeImplementationFormal languageString (computer science)Computer configurationAsynchronous Transfer ModeSocial classService (economics)Configuration spaceKolmogorov complexityComputer fileNumberCodeService (economics)Software developerGraph (mathematics)Computer architectureLine (geometry)Configuration spaceSupport vector machineSoftware frameworkPoint (geometry)Forcing (mathematics)Attribute grammarMappingInstallable File SystemTraffic reporting2 (number)Natural numberProjective planeInformationMachine codeSingle-precision floating-point formatInstallation artMereologyProcess (computing)BitRule of inferenceIntegrated development environmentBlogFrictionServer (computing)Set (mathematics)System callFrame problemOperator (mathematics)Revision controlTerm (mathematics)CASE <Informatik>Right angleGroup actionEvent horizonResultantDesign by contractShift operatorPower (physics)Computer animation
Software developerService (economics)BuildingSocial classAttribute grammarMetreRun time (program lifecycle phase)TunisMultiplication signRight angle
Group actionTexture mappingSoftware developerInformationGroup actionType theoryTerm (mathematics)CausalitySound effectPhase transitionMultiplication signProjective planeCASE <Informatik>Frame problemExtension (kinesiology)VideoconferencingSoftware frameworkPoint (geometry)Exception handlingSocial classArchaeological field surveyCodeCorrespondence (mathematics)MappingElement (mathematics)Computer animation
Software developerMultiplication signComputer fileService (economics)Shared memoryPoint (geometry)Channel capacityRow (database)Software developerWikiProjective planePerfect groupReading (process)
Software developerMessage passingView (database)Interface (computing)Game controllerPrice indexGroup actionExplosionSpacetimePhysical systemAbstractionCompilerClient (computing)NamespaceQuicksortFluid staticsReflection (mathematics)LogicType theorySource codeSoftware testingData structureSoftware frameworkTraffic reportingCycle (graph theory)FeedbackSimilarity (geometry)Physical systemINTEGRALNatural numberGame controllerSet (mathematics)Application service providerMetadataAttribute grammarGroup actionWritingFormal languageMultiplication signCombinational logicInterface (computing)Unit testingNetwork topologyInterior (topology)Design by contractUniform resource locatorRight angleObservational studySpeech synthesisCodeComputer animation
Software testingTwin primeSoftware developerDemo (music)Electronic mailing listService (economics)Computer programExecution unitAvatar (2009 film)Symbol tableError messageTask (computing)Function (mathematics)Computer-aided designString (computer science)Type theoryGroup actionData typeCone penetration testMenu (computing)SpacetimeSoftware frameworkPhysical systemReflection (mathematics)Local area networkFluid staticsMaizeNormed vector spaceExpected valueMessage passingConvex hullComputer configurationDialectCycle (graph theory)Unit testingVisualization (computer graphics)Software testingSoftware frameworkFeedbackProcess (computing)Multiplication signGroup actionMessage passingDescriptive statisticsInheritance (object-oriented programming)Type theoryAttribute grammarSingle-precision floating-point formatRight angleForcing (mathematics)2 (number)Social classExecution unitWeb pageData structureWindowBitSoftware developerMereologyFlow separationRevision controlPhysical systemProjective planeLine (geometry)NamespaceSpacetimeService (economics)Presentation of a groupInternet service providerProfil (magazine)Shared memoryVideoconferencingComputer fileWeightMusical ensembleVirtual machineGeometryLattice (order)Point cloudComputer animation
DialectSoftware developerMIDIComputer configurationData typePhotographic mosaicGroup actionFluid staticsSoftware testingSocial classAbstractionString (computer science)SpacetimeType theoryPhysical systemBit rateMessage passingParsingLatent class modelMacro (computer science)Electronic data interchangeView (database)Default (computer science)Boolean algebraOnline helpMenu (computing)MathematicsApproximationSoftware testingGame controllerMereologyMessage passingDescriptive statisticsComputer filePower (physics)MathematicsDifferenz <Mathematik>Type theoryPoint (geometry)Exception handlingMultiplication signSoftware frameworkProjective planeRevision controlGroup actionDivision (mathematics)Rule of inferenceCodeSoftware developerFluid staticsCASE <Informatik>Physical systemControl flowTrailLine (geometry)Function (mathematics)Profil (magazine)VideoconferencingObject (grammar)Inheritance (object-oriented programming)Military baseResultantService (economics)Disk read-and-write headVideo gameConsistencySocial classReflection (mathematics)Right angleGodObservational studyExistenceComputer animation
String (computer science)Type theorySoftware developerComputer iconMessage passingLie groupElectronic data interchangeFingerprintComputer programAlpha (investment)Service (economics)Component-based software engineeringSpacetimePhysical systemOrdinary differential equationElectronic mailing listCase moddingSimulationDefault (computer science)Principal idealInformation securityBedienungssystemView (database)Computer fileEuclidean vectorFluid staticsCone penetration testMenu (computing)10 (number)Installation artSoftware testingPredicate (grammar)QuadrilateralRamificationSet (mathematics)Inheritance (object-oriented programming)Service (economics)Software frameworkConnectivity (graph theory)Software testingComputer fileDescriptive statisticsFile formatPhysical systemObject (grammar)Function (mathematics)Multiplication signFeedbackConfidence intervalCycle (graph theory)Installation artSpeech synthesisLocal ringLogicLine (geometry)Principal idealCodeWeb 2.0Type theoryUniform resource locatorRight angleSoftware developerNeuroinformatikMathematicsDifferent (Kate Ryan album)File systemRoundness (object)Social classResultantMereologyArithmetic meanUser profileCASE <Informatik>FamilyGroup actionFrame problemCoefficient of determinationGoodness of fitProfil (magazine)Computer animation
Demo (music)Software developerConsistencyPersonal digital assistantException handlingSoftware testingSummierbarkeitDifferent (Kate Ryan album)VideoconferencingAngleProjective planeDisk read-and-write headGroup actionSoftware testingPhysical systemRule of inferenceException handlingRevision controlCASE <Informatik>Multiplication signPoint (geometry)INTEGRALPower (physics)View (database)Differenz <Mathematik>Negative numberComputer animation
CompilerSoftware developerLatent heatSocial classKolmogorov complexityException handlingLoop (music)Software testingCodeProduct (business)Cartesian coordinate systemCompilerLatent heatRule of inferenceLevel (video gaming)Validity (statistics)Social classServer (computing)Client (computing)Software bugConfiguration spaceService (economics)View (database)Endliche ModelltheorieProjective planeSoftware testingSoftware developerCodeException handlingCASE <Informatik>Different (Kate Ryan album)Standard deviationComplex (psychology)CommutatorSet (mathematics)Right angleProduct (business)Computer animationUML
Source codeSpeech synthesisWeb page
Transcript: English(auto-generated)
So this is a talk about my experience using conventions in C-Sharp. I'm going to talk about how I use conventions, what problems I face when I use conventions in C-Sharp, and how I overcome those problems. This is obviously not the only way of handling those problems, but hopefully you'll find it useful.
It will be slightly more theoretical at the beginning, but bear with me, it will be more hands-on in the second part. My name is Krzysztof Kosmice, and I'm a consultant. I work for a company called Redify down in Australia. This is my day job. When I come back home, I keep writing code.
Some of you may know me for my contributions to Castle Project. Are there any Windsor users in the audience? Wow, cool. Yeah, so I've been responsible for what's been happening to Windsor over the last three years now, I think 2009. I also have some other open source projects on GitHub
under my own umbrella. I will mention two of them today. And the slides from this talk and the demo will also be available on GitHub. I have a blog, I'm on Twitter. I actually prepared a custom t-shirt with my Twitter handle here so that it's easier for people to recognize me, but I will hide it because I learned
that yesterday they changed the logo, so now it's a retro t-shirt. Okay, so this is what I do. When it comes to how I do it, the approach that I like is something called zero friction development. Is anyone familiar with that term? Right, a few people. This is something that I think Oren Ini,
I and I came up with in 2008. And what it basically means is an approach of removing all of the mundane and boring and not interesting things that slow us down as we provide value in our software. In a way, this is the lean tenant of eliminate waste applied to the act of creation
of software. And during my work at different clients at Redify and while helping people on Stack Overflow or on Castle User Group with the problems that they are facing, I noticed that very often the problem that they have is related to friction. And one thing that I know from experience
that is very good at eliminating the friction is the best one that I found so far is introducing conventions to the code base. There are many definitions, and the one that I like is this, that a convention is an agreed upon approach to carrying out a particular set of tasks. So this is like driving a car.
Whether you have a Toyota, a Ford, or a Saab, every car will have a steering wheel, and the steering wheel will work pretty much the same. You turn it right, the car turns right, you turn it left, the car turns left in every single car. It's a convention that when you stop at the light, you stop at the red light and you go on the green light.
It's a very useful convention. You would probably not want to keep driving when the light is red unless you are crazy, right? Twitter is even a better example of that because it's more recent. Who's on Twitter? Obviously everyone. If you remember back to ancient days like 2007, things like mentions, things like hashtags or retweets,
they didn't exist as an explicit concept. You couldn't click them like you can in here. If you call Twitter API, you wouldn't get a collection of people that were mentioned in a tweet or hashtags that were mentioned in a tweet. This is something that people just were doing to solve the problems they were having. I want to tweet something, and this is with relation to someone.
How do I do that? Well, I put an at sign, and I put the Twitter handle of that person. Done. I tweet something and it's with relation to something. For example, a conference. Well, then I put a hash sign and some unique, hopefully relatively unique word to mean what I want to say.
And then Twitter, the company, they saw that this is something that people are doing. People take value from it. And so they took it and made it an explicit concept in their system to enrich the experience that we are having on Twitter. And you can do something similar for a development team by introducing conventions to your code base.
Very often, however, we are faced with approach like this. That if we are working with C Sharp, C Sharp is a really nice language, but people tend to have the problem that we won't be using this because it is not compile time safe. We won't be using conventions because by definition, because this is an agreed upon approach that we as a team, not the C Sharp development team,
decide to follow. This is something that the C Sharp compiler doesn't know about, so it will not be able to validate it. And C Sharp is a really nice language. It's a general purpose, strongly typed language and statically typed language. And it has certain interesting characteristics.
The strongly part means that language, the types that we create in the language don't change. So if I have a class called customer, it has a method called pay and it has a property called name, after I've compiled the code, the class will not change. It will not get a new property. It will not get new methods. The method will not change its signature and it will not suddenly become static or abstract.
The property will not change its type and suddenly name will become an int, right? And the statically typed means that all of those things, the types and the names of everything are determined during the compilation time and checked during compilation time. And some of them are also checked at runtime.
And this is great. I love C Sharp. It allows us to, it allows for very nice tooling to exist to aid us in development. So because I use a type that's in an existing assembly and the tooling knows that this type has certain set of methods or properties and it will not gain new ones or they will not change
where I mistype something and I type something that it doesn't know about, it knows that it's an error. It can tell me very quickly as soon as I type it, hey, this method does not exist on this type. The tooling can inspect the metadata about those types and can provide me with help while I write my code. We get intelligence from that.
We can also see documentation. Static typing allows for very rich refactoring support, a very smart refactoring support. Because we've got all of that metadata about this type, the tool can look at what types I have in my system, how I'm using those types, and if I want to rename something,
it can very smartly rename just the right things for me. And then we tend to rely on the compiler to tell us when we have problems with our code. When I have a type and I try to assign a property that doesn't exist, this code will not compile. And we rely on this. How many of you have written a code, made a change,
and then hit control shift B to then see that fail and then went over every single place that failed to fix it, right? Pretty much everyone. But the problem happens when we rely on the compiler too much. And then people feel very uneasy when they see code like that.
And I'm using dynamic as an example, but if you think back, even when C sharp three came out and the var keyword was introduced, do you remember the flame words that were happening on blogs and forums? Oh, var is so bad, it makes my code unreadable, it's now dynamic, I cannot validate it, right? Obviously not.
But the mere thought of there is something that the compiler will not be able to check. It freaks people out. And the same thing happens for conventions. Like I said, by definition, they will not be checked by the compiler. It makes people uneasy. They push back. We will not be using conventions because of that. But what we have to realize is that there is no way
to write a non-trivial program that will be fully validated by the compiler. Let's take this piece of code as an example. This is from a real app that I was working at. Well, this is a simplified version of that. But this is the same problem that I was facing there. This is for a client that was in a regulated industry. Certain actions in the system had to be audited.
And then we needed to show this audit in the audit UI. So we had a hierarchy of classes, audited action classes. There was the base audited action class and inherited audited actions for every action that was being audited in the system. And then we had a corresponding hierarchy of DTOs. And the problem here is how do we take
those audited actions and then convert them to DTOs so that we can display them in the UI. And this is one way of doing it. We get all the audited actions and then we interrogate them. Hey, are you this concrete type? No, I'm not. Well, then maybe you're this concrete type. Well, not really. Oh, maybe this one. Well, actually I am.
Okay. Then I will create a corresponding audited action DTO. I will downcast you to your real type and then I will copy this property to DTO, that property to DTO, and then add the DTO to the collection and then move on to the next one. So the rule here is that we need that if
for every single concrete audited action type. But if you forget to add it for one, will the code compile? Well, obviously it will. Will it work? Well, it I guess depends on your definition of work. Depending on what's at the end of this for each loop,
it may either fail at run time, give you an exception, and embarrass you during the demo. Or it may not fail and then the audited action will just not appear in the UI. And then you may be fined for that because as much as the auditor is concerned, well, your code is not auditing this thing and it should have.
And this is because C-sharp is a general purpose language. It has a general purpose compiler. It does not know about those rules that you have in your system. So why don't we just forget about it for a moment? Why don't we just let go of this thing that the compiler can help us? Let's forget about the compiler.
Let it sink in for a moment. Let's just put the compiler away and forget that the compiler does any validation because it actually doesn't do it as its primary purpose. The primary purpose of the compiler is take your C-sharp code and do two things with it. Generate IL and generate a set of metadata
about all the types that you have in your system. And as it goes through that and it sees something that it doesn't like and it doesn't know what to do with it, then it delegates to you. Hey, there's something I don't like. I don't know what to do with it, fix it. And then we have compilation errors. But that's not the primary purpose of the compiler. So let's not over rely on it, okay?
And then let's step back and try to rethink how we use conventions and how perhaps using conventions can help us in this scenario. And this actually already is a convention if you think about it. It is an agreed upon approach to solving a particular problem. We've got set of audited actions. We need to convert them to DTO
so that they can be displayed in the UI, right? And it clearly is one agreed upon way of doing that with those ifs and add DTO methods, right? It's just not a very good convention. Why is it not a very good convention? Well, first of all, this code is horribly,
horribly boring to write. How many of you attended Hadi's session in the morning today, right? So he was talking about it. We are not becoming developers to write this sort of code. Would you be excited if you were waking up in the morning thinking, wow, I'm going to write some ifs and some add DTO methods and some mapping code.
Wow, this is so awesome, right? We don't do that. You don't need a developer to write it for you. You need a typist, someone who can type. You can explain it to a child who doesn't know C Sharp or to somebody, not necessarily a child. I'm not advocating child labor, by the way.
But you can explain it to someone who doesn't know C Sharp who's not a developer and they will be able to do it because the algorithm for doing that is very simple. Let's look at another example. This is from the same system. We had a set of reports that we needed to render and display to our users.
So we take a report from the database, an instance of a report class, and then we need to locate a corresponding RPT file, a Crystal Reports template file for it. And then we render it. So how do we approach this problem? How do we locate the template knowing that we have a given report?
And the path to that file looks something like that. It's on the server, and then basically we as a development team had the liberty of where we want to put those files. The server would be changing between different environments, but it was completely up to us on how we structured that.
So we can approach it in a very similar way like with the previous example. We can hard code all of the mapping between the report, say based on ID, and the path, and then just randomly put all those files somewhere. If we are smart, we'll probably put it in the config file because this is cool,
and then we can change it between different environments. But this is again us doing the job, right? This is a convention in a way. It's a convention that says we locate RPT files for reports because we look them up in the config. But this is again a convention that we are doing the job. So why not turn it around and devise a convention
that will do the work for us? And this is what we did. This is how we implemented that method. That based on information from the report, we devised the rule that we'll use information from the report to create a path, and this is where we are going to put the file. And we will consistently follow it for every part.
And then with this small piece of code, given the report, we can reconstruct what the path is without having it hard coded. And notice how much different this code is from the previous example. It's so much simpler. Again, even if you're not a developer, you could probably understand what it's doing, right?
It's so much less code. There's so much less glue code, all those ifs and curly braces and the semicolons and everything. And one other important point. It doesn't use a framework. When people think about conventions,
they often think in terms of frameworks. I'm using a framework, the framework forces me to use conventions, therefore I use conventions. But this is not necessarily how it has to happen. Don't constrain yourself artificially to using conventions only when you are dealing with a framework. If you were actually to introduce a framework here,
we could not, I don't think we could easily make this code simpler. It would probably only make it harder to read and harder to maintain. This is as simple as it gets. And I'm not saying that you shouldn't be using frameworks. A well-designed framework, framework design with conventions in mind can really help you limit the number of lines of code
that you are writing and can really simplify your architecture very nicely. WCF is a really nice example of the opposite, really. This is, how many of you have seen services like that? Right?
If you go to MSDN or search on blogs on how to apply behaviors to WCF services, this is what you are going to get. And this is what we were following on the same project. We needed services, we needed to apply some behaviors to them. So, well, that's what we did.
We followed what MSDN says, what the blog says. And we needed a behavior. Well, let's put an attribute there. We need another one. There's another attribute for that. We need a behavior on an operation. Well, put it on the method. And we had plenty of services, and each of them, because we were consistently applying the same set of behaviors, this was our convention,
every single service looked like that. If we needed to add a new service, you would find an old one, copy paste all the attributes, and then we would be done. And I'm not, this is just one part of it. I'm not showing you everything. Because let's not forget about the XML configuration. All of the ABCs, address, binding, contract,
all of the other things you put in XML. And this was hosted in IIS, so we also needed the SVC file for it, for every single service that we had. So this is obviously not pretty, right? And you would think there has to be a way to do it better. There has to be a tool that would allow us
to apply the same behaviors and configure the same services in a simpler way, without us doing the job again, but the tool doing the job for us. And this is what we did. We started using Windsor's WCF facility, and then in this small amount of code,
we were able to register all of our services in the IOC container, and then because WCF facility is a tool specifically designed to configure your WCF services by convention, with a small amount of code, with this configure method, we were able to say, those are my WCF services, and configure them in this way,
make them hosted in IIS, open them eagerly, publish max endpoint, and all the other things. And this single piece of code was responsible for registering all of our services, applying all of the behaviors to them. The only thing that we needed to add on top of that was a few lines in our global file
so that we didn't have to have SVC files, we were using routing instead to expose the WCF services. And now I will share a secret with you. We did that only after nine months of development. For the first nine months, we were doing the other thing with the attributes and with XML.
And why we did that has to do with this graph. This is the graph that shows us on the x-axis the size of the problem that we're dealing with. In this case, this is the amount of WCF services that we have, and on the y-axis, the amount of code that we write to support this problem. And the red line is how much code we have
if we hard code all of that with the attributes, and the blue line is amount of code that we have when we have automated convention. And the important bit is the lower left corner. This is when you have very small number of your services,
probably one. Because when you do that, it's actually less code, or can be less code, to register things by hand like that to expose to other behaviors by just slamming in an attribute. It's less code to just put an attribute than it is to write that installer that I just shown you.
But by doing that, you set up a precedent, because then the next guy that comes along and wants to add another service, or wants to add another behavior, sees that code, and because we as developers generally tend to be nice people, we just follow what the other guy said. So we are just going to do the same thing. And then another behavior comes along, and another service comes along, and very quickly, we are going to end up somewhere here.
And only at this point did we realize, or maybe we actually realized this much sooner, but at this point we actually decided that we are going to change that. Why did we do it only then? Well, that's the human nature to first of all
follow the established convention, and another one is that there's friction, there's a small amount of friction in actually having to go and re-architect all of the WCF services that we have. Although I must tell you that removing all of those attributes and all of that XML, specifically XML, and SVC files was really liberating and really nice.
But this is obviously not the end of it. Because it's not just about having more code or less code. Another thing is that conventions are implicit. If you have WCF service with some attributes on it, by just looking at the WCF services, you see that those attributes are being applied to that,
but that those behaviors are being applied to that WCF service. If you do it with Windsor, WCF facility, or any other tool like that, you don't see this. It's just a normal class. And you have to trust that those behaviors are going to be there at runtime. You do not see this. This is like standing on the glass floor.
This is a picture from a building in Chicago, I think, from a balcony that has a glass floor, and then you look down, and there is like 100 meters of nothing, and a piece of concrete waiting for you, right? And I don't know about you, but this picture freaks me out, right? And this is what happens with people
when you are using behaviors. Like they look at the service, and there's nothing there. It's like here. I step out, and there's nothing there. And I don't want to feel like Wile E. Coyote from the Looney Tunes cartoons, right? That he always runs and runs and runs, and there's nothing, and he just looks at the camera and says,
and then falls down, right? We don't want to feel like that. But this is the feeling that we often, not always, this is the feeling that we often get. So let's revisit that. Let's go back to the first example, and how we can change that. So we can go and use a framework again,
in this case, that would do the mapping. In this case, this is using Cartographer, which is a framework that I'm building, but you can just as well use any of the other frameworks, like AutoMapper. So what it does is it says, well, I have the same audited actions. Now I will delegate to a tool to convert them to audited action DTOs.
And the only thing we need to do in this case is, there is an extension point called ITypeMatter that will be called down for every element in the collection and will be passed, among other things, the actual concrete type of the audited action. And then using conventions based on the name, we find the corresponding DTO type.
But it's obviously not immune to failure. Things can still go wrong. If I have an audited action, but I forgot about creating the DTO, well, I will get a failure at runtime, still. So even though I have less code, it didn't really help me to avoid problems.
Actually, it's still bad because we still have this glass floor, this thing that I cannot really touch. There is an exception somewhere in this weird code that I don't really know what it's doing, and the cause is far from the effect. It's far from the effect in terms of in time because it fails at runtime,
but the cause of this is that when I created my audited action, possibly yesterday or a week ago, I forgot to create the audited action. And it's far in terms of code because it's failing somewhere in my audit service, but the reason for that is because in a different project, I forgot to put a class. So how do we solve that problem?
Discipline. We will make developers remember. Every time you create audited action, create a DTO. Solved, right? Perfect. We can go home now. Well, not really. Well, obviously, this may kind of work if you have one conventions, but you will never have just one.
You will have plenty. So you will have the WCF services and behaviors, and you will have the RPT files, and you will have the reports, and plenty, plenty of others, and human brain has only certain capacity, so we'll obviously not be able to remember everything. Well, then there's a better solution. Let's create a wiki and put it in SharePoint
and make people remember to read it. Right? Who has seen projects do that? Obviously, yes. Well, again, the same problem. It may help you to a point, but then you have to remember that there is something that you are supposed to read and where it is, right?
So again, there's the problem that we are forced to do the work. We are forced to remember. We are forced to go and look and find something in SharePoint and find the wiki and read it, which may also be a problem in itself, and understand it. So we need more visibility. This is the problem. This is the real underlying problem with using conventions in those scenarios,
that the convention is doing something, but we don't really know what it's doing, why it's doing it, when it's doing it, right? So how can we raise the visibility? How can we remove the glass floor? Well, one way of doing that, that most tools and most teams
that I've worked with are doing is, we need something tangible to begin with. If we want to go and look at those conventions and be able to see if they are being applied correctly, if they are being applied at all, and what they are doing, we need something tangible. So let's turn to the C-sharp language, to the static nature of C-sharp,
and remember that it creates the I-L, the C-sharp compiler creates the I-L when it generates, but it also creates a set of, very rich set of metadata about those types, and about relationships between those types. And we have API to do that. It's called reflection. So why don't we structure conventions in a way that makes it easy for us to inspect
the things that we care about using reflection? So I'm sure most of you are familiar with this example. This is WCF, this is, sorry, ASP.NET MVC, and it has convention like that. It uses base type, or actually an interface,
to find types that it cares about. It cares about controllers, and a controller is the type that implements an I controller. We can very easily inspect those sorts of things with reflection. We can use generics. We can say that type that is data source for my monthly revenue per client report
is type that implements I report data source of this report. Again, something that can be easily checked with reflection. We can use custom attributes. This is what WCF does to denote the contracts, or this is what MAF is doing. We can use the name. This is what we did with the audited actions,
that based on the name, naming convention, we match one type to the other. And you can even use location. If there are types that don't really have that much in common with each other, just put them, but they serve the same role, or similar role in the system. Just maybe put them in the same name space.
So where are we going to do that? Where are we going to do that reflection thing magic to inspect those things? Well, first of all, what we care about is we care about very short feedback cycle. So we want to have a similar experience to just writing some piece of code, clicking Control-Shift-B,
and then the compiler doesn't compile. We want to do something similar experience for this. We want a similar experience for our conventions. Well, you have an audited action, but there's the only thing. Would you like me to help you with that? So how can we do that? Well, one way of doing that is,
okay, let me step back. How many of you write tests? Right, pretty much everyone. So if everyone writes tests, and tests run right after we compile the code, hopefully, before we commit, hopefully again, why not use the tooling that we have for testing? We usually have rich IDE integration.
We have tools that run the tests. Some of us use tools that run tests continuously in the background. Why not leverage that to run our conventions? Why not use testing frameworks to not really run tests that validate the logic and behavior of our system, but also write tests that validate
the structure of our system, right? Why not leverage the unit testing or other testing framework that we are using to do that work for us so that we get a very short feedback cycle and we get a really nice experience and really nice Visual Studio integration?
So now I'm going to show you how we did that, okay? And we don't really have that much time, so perhaps instead of writing this, I will just do the shorter version and show you the final version of what it looks like.
So first problem is, where do we put them? Where do we put those tests? And some teams want to create a separate test project, that has just conventions to make it explicitly separate. The approach that I take is generally I just have a conventions folder in my test project,
where all my conventions are. The important bit is that you stay consistent. There are two roles that those tests are going to serve. First of all is tell you where the problems are and when you hit those problems.
And second of all is serve as executable documentation. Serve as replacement for that page in SharePoint. So a very important thing is to install all of your Windows updates before you do the presentation. And another very important thing, even more important,
is to make them as readable as possible so that when someone comes along, they can very easily look at your conventions and learn about what the conventions are, how your system is structured and how it works. So for that, based on the project that I was working on,
I have a small NuGet package, which is called convention test. It says local here, but it is also available on NuGet. And this is a very simple thing that doesn't have much behavior in it. All it does is it provides structure and plugs into any unit to allow me to quickly and simply write those tests.
You want to make them as simple as possible because if you make them complicated, people will not write them. And we really want to care about the experience that developers on the team have when writing those conventions. And also because they are supposed to be your executable documentation. They need to be very easy to read.
So this is a very simple package. It's code only, it's not a binary, and it pulls in some dependencies and you need wins or an approval test. I'll talk about this in a second. And what it allows me to do is it allows me to write tests like this. So first of all, this is how I name my tests.
There is one test per class. And the way I structure them here, the way I name them, is I want to give out what the convention is by just looking at the name of the test. So you should be able to know what the conventions are just by looking at what the conventions class is
I have in this folder. And the way they are implemented is, for example, let's look at each audited action has a DTO. So I have this convention test base class here. This is what comes in the NuGet package. And what it forces me to do, it's an abstract class,
and what it forces me to do is forces me to overwrite this single method. And this single method is just so that I return something called convention data, which specifies what my convention is. And it says that types, where T is a concrete audited action,
which means it's a class and it's not abstract, must have corresponding DTO. And this is a very simple thing that looks up, by the convention, deconstructs the name of the DTO, tries to locate the DTO type, and it tells me whether it found the type or not. And now that I run this test,
the way it's structured is I actually notice that this is not an NUnit test. There is no test attribute on the method, there is no test fixture attribute on the class, and there is no ReSharper helpers for running those tests. I actually run those tests by, the test itself is in this run file,
and it's a parameterized test. So this is the test itself, and it takes those IConvention tests, those types here, and then it runs them. And this is so that I am forced, or the developers that do this are forced,
to have one single convention per class. This is a different one, okay. So now, if I run that, each audited action has a DTO, well, the test will pass because I am conforming to my convention, that's great. But the interesting thing is if I, for example, go,
and I rename one of the DTOs, or I rename one of the audited actions, right. So what happens now, if I run the test, if I run the test, that the test will fail.
It fails and it tells me invalid types found. And it's even nice enough to tell me what the type is. And it tells me that each audited action has a DTO. Notice that it doesn't even have underscores. This is to make it easier to read for people. The way I name the test, I plug into a unit and I even remove the underscores.
So this is nice. We can even improve it better. We can make it better. Because it's not just about telling people that there is a problem. We want to go this extra step and tell them how to fix that problem. Or we want to tell them exactly what the problem is.
You've seen that I said, well, there is an audited action that is missing a DTO. Well, okay, so what, right? So I can say, well, this is the failed description. This is where I can say the first line of that message, which says, well, there are audited actions
that have DTOs missing. If we have that, we pay a lot of money. To someone, right? So we explain what the problem is and why it is a problem. And we can even be nice enough to say,
well, okay, there is a DTO missing. I don't know what that DTO is supposed to be, where it's supposed to be. So I can specify a failed item description, which will construct a message and it will tell me, well, what the expected DTO type name is for that particular type. So if I run this test again, and if we look here,
the message now will be much more useful. It will say, there are audited action DTOs missing. There is all that message that I put in, and it tells me, it expects an audited action DTOs, which is the namespace bar audited action DTO, for this audited action type. So this is actionable now. Now I know where I'm supposed to be looking at.
Now I can go and look for that bar audited action DTO if it's there or if it's not there, right? So this is one part of the coin. Another part of the coin is, I started by looking at audited actions, but what if a new developer comes along and it knows that it looks from the UI
and it knows there are all of those audited action things that it needs. So it will create an audited action DTO only, and now it will expect it to work. There is this convention magic thingy that will make it work, right? I don't really understand what it's doing, but it will make it work. And he expects this to happen, but it doesn't. Now this test will not fail,
because the audited action exists, because there is no audited action for this DTO, right? So we need another test. We need a test that will tell this developer, hey, you've created a DTO. Now we also need a corresponding audited action. So this will be a very simple test.
This one. This will be a very simple test that will do the same thing, but from the other end. And this is important to do both of them, so that regardless of which approach people take on the project, those tests will fail and will fail consistently.
So this is nice and easy, and it all works, except in real life. Not all rules are all black and white. So in this case, we can say, well, every single audited action, no exception, needs a DTO.
But most rules that we have have exceptions to them. One example that we've had is, we had a rule that said, well, DTOs, they are really just data containers that we pass along, so they should have no methods, and they should have no static members, because why would they, right? But for particular aspect of the system, when we were integrating with a legacy application,
we were not in control of the DTOs, and there was a DTO type that, well, was not conforming to that rule. It had a static method that looked something like that. It had a flag, and we needed to know whether it's active or not, what the value of it is.
So we had a convention, but it had an exception. Well, it kind of breaks this, our nice approach that we've had there, because, well, it doesn't seem like there is an easy way to say, well, every single except for this one. Well, we can do that when we go,
when we go here, and we say, well, every type that is a concrete audited action needs to have a DTO, or in that case, would be must not have a static member if it's a DTO, or we might say, well, except if it's this one or that one or that one. But this kind of misses the point,
because the point of this test, among other things, one of the primary points of this test is to be readable. People will come to the project, people will read that code, and they are supposed to understand it as quickly as possible. People will come back to parts of the code base that they haven't touched in six months, and they should be able to read it and understand what it's doing.
So we want to make those tests as simple as possible. So how do we do that? How do we accommodate those exceptional cases that don't really conform to the convention we've established? And a way to do that, so this is the convention that we have.
So it has the same base class, and it returns this type, and it says, well, every type that name ends with DTO, this is how we define our DTOs, must have no static members. So this is a method somewhere here below that looks for static types using reflection. Very, very simple.
We have the file description, we have the file item description when we say, well, which type and which members are static that we find on it. And then I have this additional method called that says with approved exceptions. And then we say, well, unless we pass string status. So this is a part of that, this is a piece of text that explains why the exception exists.
And if I run this test now, it will pass, but it will fail. Will it fail? It will pass, but it will fail if I do this. So let's disregard what I just did. We can cut it out, right? Okay. So I run this test now.
And this is my test. And it will fail, but it will fail in a very interesting way. And this will blow your mind if you haven't seen this before. Well, at least I know that it blew my mind the first time I've seen this. Notice what happened. The test failed and my div tool popped out.
My div tool popped out and it has the text, this exception message text that my convention is creating on one side and it has an empty file on the other side. Right? And notice the file. This is file named after the test that says received.
And on another end, it has the same file called approved. So this is the framework that I'm using here. I didn't create it, but it is really great. You should check it out. It's called approval tests. An approval test, what it does is it generates an output of your test, an expected output of your test.
And then you look at the output and you approve it. And this is the, and it then gets persisted and then you compare the subsequent runs of your test with the approved version. And so it will pass if nothing changes in the output, but it will fail if something changes. So in this case, I did not have that file
because I just removed it. So the approved file is an empty file. And it says, well, static members found on a DTO types on this DTO and this method. So now I can do very quickly, without obscuring the code of my test, is using all the power that my diff tool gives me,
I can say, I approve of that. This is correct, this is fine. This is what it's supposed to be. We don't control this test. We don't control this, sorry. We don't control this DTO. This DTO is coming from a legacy system. So I now save that, and I now click commit.
And this is also important bit. I click commit, I commit that file to my repository so that all the other developers have the same approved file. And as I commit it, in the exception message, I say why I did that, why this is okay for this file to be, for this type to be different than the convention specifies.
And this is really powerful because what it, what it gives you is it gives you a trail of those changes. As the project grows, if you find something you see that doesn't conform to the rule, you're not sure why. You can always look at that file in your blame tool,
you can find the exact line that corresponds to that type, and you can hopefully see a helpful message that will explain you, this is okay because of that. Because this is a DTO from a legacy system, we need to integrate with it, we are not controlling how it works. Right? And this is really powerful, and you can use that for many other things as well.
You can use that not just for the things that you control, but for things that are out of your control. You can use that to, let's go back to this example of WCF and behaviors. There is a framework that we are using that applies behaviors to the WCF services, but we do not see that.
But why not use this, why not use this tool, why not use approval testing, and then generate an approved file that all it does is it lists the behaviors that are applied to every single service that we've got. And then we can very easily open that file and look at it.
And if something changes, if a new behavior comes along or if a behavior for whatever reason does not get applied, this test will fail, and your digital will exactly show you in which lines it failed, which behavior existed and doesn't exist anymore. This really is very powerful. If you will remember one thing from that talk, remember this.
Use approval test to validate things, to raise the visibility of the things that you do not normally see. So for example, what we could do is if you are using Windsor as your container, or any other container for that matter,
I'm using Windsor because, well, I use Windsor, but you can do the same thing with every other container. So for example, the problem with containers is there are those components registered in them, and they expose services and they have lifestyles, but you kind of have to know the place where they are and you need to know how your framework works exactly.
Well, why not put it all out there in an approval test and have an approval test with a proofed file that will list all the registered components in your container? So you can write a test that will generate you this. It will register everything in your container,
and then it will tell you, well, you have four components. You have audit service class, which exposes iAuditService as a service. It's transient. You have imapper. I don't know what it's implemented by because so it's laid bound, and it's a singleton. I have local file system, and I have time service. They are both singletons.
This is very empowering, very useful, and it increases the confidence of people on your team, the fact that they can look at those things and they can know what gets registered. And I will not even tell you how many times I found this very useful where I was trying to diagnose something, and then I could look at the file, and I immediately could see whether a type is registered
or if it's not registered. I expected it to be a transient, and it's a singleton. And you can take it even further. With many frameworks, you can inspect statically things that are wrong with it. So for example, with Windsor, you can statically inspect the components that Windsor thinks are misconfigured,
that they have dependencies that are missing, right? So this is my first file, which says that the following components come up as potentially unresolvable and it's empty. So it's good. All of my components can be resolved according to Windsor. But if I then go,
and so let's find a component. Let's see, let's take it to service. So I add a dependency here, one that's not being registered, right? Say I principal, that's fine. Okay, so I now run this test.
So imagine I'm a normal developer, I work with something, I expected that I principal will be registered, I think it's fine. So I just added, I keep working and I ran my test, and now a failure. I didn't expect it to happen. This helped me from committing not working code, even without running that code.
If this was a service that is called only once in a blue moon, if I didn't have that, think what would be the chances to finding that, right? And using this tool, using this approach, I've shortened the feedback cycle. I immediately, right after compiling and running my test, know there is a problem. And Windsor is even nice enough to tell me what the problem is.
It tells me some dependencies of this component could not be static. Let me make it wider. Could not be statically resolved, right? It tells me which component. I have audit service, which is waiting for the following dependencies. For iPrincipal. Well, and iPrincipal has not been registered.
This really is very powerful. And you can use it not just for your IOC containers, you can use it for every single framework or for every single convention that you have in your system. Make the implicit things that happen in those frameworks explicit. So for example, if you use Fluent and Hibernate
to generate your mappings, why not output them to a file, the XML that gets generated, and then make an approval test out of it, right? And then it will dramatically increase the confidence that your team has in those tools, and it will make them much easier to work with. It will decrease the amount of problems
that you are facing. And I'm speaking from experience, right? And we've got some time, so maybe I will quickly just show you how those Windsor component, Windsor test works. They are slightly different, and that's why this framework has dependency in Windsor.
So for this case, I have the base class is Windsor convention test, which returns Windsor convention data. And then it takes a configured container. So I create the container, I register components using installers from my other assembly, not the test assembly,
but the one that has the actual code. And then in this case, by definition, I don't need to say anything. It will just look at all the handlers. So all I really need to do is just nicely format the description for each of them. The other test, the one that lists the components that are misconfigured,
this is using the diagnostics from Windsor. So it's a Windsor convention test of hypotentially misconfigured components diagnostic. Windsor has a set of those for different things. For example, it will tell you when you have a pair web request, when you have a singleton that depends on the pair web request component,
which generally is a really bad thing. It will tell you when you have service locator usage or things like that. So this again, is very, very simple thing. All the logic is not really relevant hidden from you in the base class. So all you really care about is you then again, create your convention data,
you put the container in it so that it can inspect it. And then you say, you just specify the failed description and the failed item description. So this is very simple to work with, very simple to read, and it tells you that this is only potential. So this is yes, this is an important thing to understand
when working with Windsor that those are potentially misconfigured components. And this is again, why we are using approval tests for that, because it may be okay for certain of those things to exist. Windsor only statically can go that far. So it will tell you, well, I think that you will not be able to resolve this object
because it's missing some dependencies. But if the dependencies are provided dynamically at runtime, it will work just fine. So you can then approve that and you can say, I know of that, I realize what the ramifications are, but this is fine, that's correct. Let's proceed.
And a very important things happened here. Okay, let's first recap what I did. So no matter how, no matter if you take this approach or a different approach, I'm not trying to sell you that nougat that I've just shown you. This is just what I've been using on different projects and it has proven useful to me,
might not necessarily be useful to you. But whatever you do, be consistent. Remember that this is supposed to be read by many people over and over during the lifetime of your project. So make it consistent, make it easy to read, because this is your executable documentation. This is replacing, this is executable version
of this SharePoint document that you've got. Least the offenders, don't just say that, well, there are audited actions that are missing DTOs. That's it. Just say which of them are missing DTOs. Explain what the problem is. Why is it bad that they are missing the DTOs?
What's going to happen if they don't have one? Maybe tell people how to solve that. Or maybe you can just say, well, go talk to John. That can also be fine. Test negative cases. Don't assume that people will approach the problem from the same angle every single time. You may start by creating your audited action first,
and then if you've got tested approaches, it's only from the audited action DTO, and it will not fail. And then people will be left scratching their heads thinking, why is it not working? Use approval testing for exception to the rule. And again, this is a very powerful tool. And the integration with your diff tools, if you use powerful diff tools,
will really easily and nicely visually show you what has changed, where it has changed. This is a really powerful tool. And use approval testing to make what's implicit explicit. Remove the glass door. And then people will not freak out when working with conventions in your system.
And what it gives you is, we no longer have just the basic validation at the syntactic level that C-Sharp compiler is giving you. This will create application-specific compiler that will validate the rules that are specific to your application. And those are not the only benefits of conventions.
Conventions also establish common ways of handling recurring classes of problems. So for example, this is how we set up our client server configuration. They help you reduce complexity, but by having just one way of solving one particular set of problems.
We do it this way and only this way. And then if you have tests, the tests will tell you where you've deviated from that single way, so that you can fix it, you can adjust it. And they facilitate discussion when you've deviated. You've got a test, you've got a new class that is, say, a new service or a new view model, for example,
just not to stick to WCF for too long. So you've got a different view model that is different than all the other view models that you've got. Using conventions, this facilitates your discussion because you've got your convention and then your test fails, telling you, well, there's something different about this view model.
There is an exception in the test. Well, then you hopefully grab someone who has worked with them, or maybe the senior person or some other developers, you gather together and you look at it, and you discuss, why is it different? Is it okay that it's different? Well, maybe we just approve that exception using approval test and go on. Maybe the convention is no longer right.
Maybe the convention has only very narrow look at the world, but this is a valid case for this to happen, so you change your convention. Or maybe this is simply a bug and then you just change the view model and it conforms to the rule. But what is really important is that it makes us talk to other people.
Otherwise, without those tests, we would just say, oh, well, commit, move on, right? So what I found by using conventions is that they make happier developers because we write less brain dead code. We have more fun code to write. We outsource all of the boring things
to tools and to conventions. They do the work for us, and we concentrate on the actual problems that our customers want us to solve. And with that, we deliver better projects sooner. And this is it. We've got three minutes left, so if there are any questions, I will take them now. And if not, well then, thank you very much.
Oh, there's a question there. I'm sorry, I cannot hear you. Can you speak up? Yes, source code will be available on my GitHub page.
Okay, then thank you very much. Thank you.