Closed. This question needs to be more focused. It is not currently accepting answers.
Closed 2 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
Any code can be reused in a way or an other, at least if you modify the code. Random code is not very reusable as such. When I read some books, they usually say that you should explicitly make the code reusable by taking into account other situations of code usage too. But certain code should not be an omnipotent all doing class either.
I would like to have reusable code that I don't have to change later. How do you make code reusable? What are the requirements for code being reusable? What are the things that reusable code should definitely have and what things are optional?
See 10 tips on writing reusable code for some help.
Keep the code DRY. Dry means "Don't Repeat Yourself".
Make a class/method do just one thing.
Write unit tests for your classes AND make it easy to test classes.
Remove the business logic or main code away from any framework code
Try to think more abstractly and use Interfaces and Abstract classes.
Code for extension. Write code that can easily be extended in the future.
Don't write code that isn't needed.
Try to reduce coupling.
Be more Modular
Write code like your code is an External API
If you take the Test-Driven Development approach, then your code only becomes re-usable as your refactor based on forthcoming scenarios.
Personally I find constantly refactoring produces cleaner code than trying to second-guess what scenarios I need to code a particular class for.
More than anything else, maintainability makes code reusable.
Reusability is rarely a worthwhile goal in itself. Rather, it is a by-product of writing code that is well structured, easily maintainable and useful.
If you set out to make reusable code, you often find yourself trying to take into account requirements for behaviour that might be required in future projects. No matter how good you become at this, you'll find that you get these future-proofing requirements wrong.
On the other hand, if you start with the bare requirements of the current project, you will find that your code can be clean and tight and elegant. When you're working on another project that needs similar functionality, you will naturally adapt your original code.
I suggest looking at the best-practices for your chosen programming language / paradigm (eg. Patterns and SOLID for Java / C# types), the Lean / Agile programming literature, and (of course) the book "Code Complete". Understanding the advantages and disadvantages of these approaches will improve your coding practice no end. All your code will then become reausable - but 'by accident', rather than by design.
Also, see here: Writing Maintainable Code
You'll write various modules (parts) when writing a relatively big project. Reusable code in practice means you'll have create libraries that other projects needing that same functionality can use.
So, you have to identify modules that can be reused, for that
Identify the core competence of each module. For instance, if your project has to compress files, you'll have a module that will handle file compression. Do NOT make it do more than ONE THING. One thing only.
Write a library (or class) that will handle file compression, without needing anything more than the file to be compressed, the output and the compression format. This will decouple the module from the rest of the project, enabling it to be (re)used in a different setting.
You don't have to get it perfect the first time, when you actually reuse the library you will probably find out flaws in the design (for instance, you didn't make it modular enough to be able to add new compression formats easily) and you can fix them the second time around and improve the reusability of your module. The more you reuse it (and fix the flaws), the easier it'll become to reuse.
The most important thing to consider is decoupling, if you write tightly coupled code reusability is the first casualty.
Leave all the needed state or context outside the library. Add methods to specify the state to the library.
For most definitions of "reuse", reuse of code is a myth, at least in my experience. Can you tell I have some scars from this? :-)
By reuse, I don't mean taking existing source files and beating them into submission until a new component or service falls out. I mean taking a specific component or service and reusing it without alteration.
I think the first step is to get yourself into a mindset that it's going to take at least 3 iterations to create a reusable component. Why 3? Because the first time you try to reuse a component, you always discover something that it can't handle. So then you have to change it. This happens a couple of times, until finally you have a component that at least appears to be reusable.
The other approach is to do an expensive forward-looking design. But then the cost is all up-front, and the benefits (may) appear some time down the road. If your boss insists that the current project schedule always dominates, then this approach won't work.
Object-orientation allows you to refactor code into superclasses. This is perhaps the easiest, cheapest and most effective kind of reuse. Ordinary class inheritance doesn't require a lot of thinking about "other situations"; you don't have to build "omnipotent" code.
Beyond simple inheritance, reuse is something you find more than you invent. You find reuse situations when you want to reuse one of your own packages to solve a slightly different problem. When you want to reuse a package that doesn't precisely fit the new situation, you have two choices.
Copy it and fix it. You now have to nearly similar packages -- a costly mistake.
Make the original package reusable in two situations.
Just do that for reuse. Nothing more. Too much thinking about "potential" reuse and undefined "other situations" can become a waste of time.
Others have mentioned these tactics, but here they are formally. These three will get you very far:
Adhere to the Single Responsibility
Principle - it ensures your class only "does one thing", which means it's more likely it will be reusable for another application which includes that same thing.
Adhere to the Liskov
Substitution Principle - it ensures your code "does what it's supposed without surprises", which means it's more likely it will be reusable for another application that needs the same thing done.
Adhere to the Open/Closed Principle - it ensures your code can be made to behave differently without modifying its source, which means it's more likely to be reusable without direct modification.
To add to the above mentioned items, I'd say:
Make those functions generic which you need to reuse
Use configuration files and make the code use the properties defined in files/db
Clearly factor your code into such functions/classes that those provide independent functionality and can be used in different scenarios and define those scenarios using the config files
I would add the concept of "Class composition over class inheritance" (which is derived from other answers here).
That way the "composed" object doesn't care about the internal structure of the object it depends on - only its behavior, which leads to better encapsulation and easier maintainability (testing, less details to care about).
In languages such as C# and Java it is often crucial since there is no multiple inheritance so it helps avoiding inheritance graph hell u might have.
As mentioned, modular code is more reusable than non-modular code.
One way to help towards modular code is to use encapsulation, see encapsulation theory here:
http://www.edmundkirwan.com/
Ed.
Avoid reinventing the wheel. That's it. And that by itself has many benefits mentioned above. If you do need to change something, then you just create another piece of code, another class, another constant, library, etc... it helps you and the rest of the developers working in the same application.
Comment, in detail, everything that seems like it might be confusing when you come back to the code next time. Excessively verbose comments can be slightly annoying, but they're far better than sparse comments, and can save hours of trying to figure out WTF you were doing last time.
Related
I wonder what sort of things you look for when you start working on an existing, but new to you, system? Let's say that the system is quite big (whatever it means to you).
Some of the things that were identified are:
Where is a particular subroutine or procedure invoked?
What are the arguments, results and predicates of a particular function?
How does the flow of control reach a particular location?
Where is a particular variable set, used or queried?
Where is a particular variable declared?
Where is a particular data object accessed, i.e. created, read, updated or deleted?
What are the inputs and outputs of a particular module?
But if you look for something more specific or any of the above questions is particularly important to you please share it with us :)
I'm particularly interested in something that could be extracted in dynamic analysis/execution.
I like to use a "use case" approach:
First, I ask myself "what's this software's purpose?": I try to identify how users are going to interact with the application;
Once I have some "use case", I try to understand what are the objects that are more involved and how they interact with other objects.
Once I did this, I draw a UML-type diagram that describe what I've just learned for further reference. What happens after depends on the task I've been assigned, i.e. modify the code, document the code etc.
There is the question of what motivation do I have for learning the new system:
Bug fix/minor enhancement - In this case, I may focus solely on that portion of the system that performs a specific function that needs to be altered. This is a way to break down a huge system but also is a way to identify if the issue is something I can fix or if it is something that I have to hand to the off-the-shelf company whose software we are using,e.g. a CRM, CMS, or ERP system can be a customized off-the-shelf system so there are many pieces to it.
Project work - This would be the other case and is where I'd probably try to build myself a view from 30,000 feet or so to know what are the high-level components and which areas of the system does the project impact. An example of this is where I'd join a company and work off of an existing code base but I don't have the luxury of having the small focus like in the previous case. Part of that view is to look for any patterns in the code in terms of naming conventions, project structure, etc. as this may be useful once I start changing some code in the system. I'd probably do some tracing through the system and try to see where are the uglier parts of the code. By uglier I mean those parts that are kludge-like and may have some spaghetti code as this was rushed when first written and is now being reworked heavily.
To my mind another way to view this is the question of whether I'm going to be spending days or weeks wrapping my head around a system like in the second case or should this be a case where it hopefully takes only a few hours, optimistically that is, to get my footing to make the necessary changes.
So DRYing up code is supposed to be good thing right? There was a situation in one of the projects I was working on where there were certain models/entities that were more-or-less the same except the context in which they were being used. That is, Every such entity had a title, descriptions, tags, a user_id etc and some other attributes. Hence their CRUD actions in their respective controller looked pretty similar.
My manager argued that its repetition of code and needs to be DRYed up. Hence he came up with CRUD ruby module that when included took care of CRUD actions for the controllers of all these entities. But eventually, Simplicity was compromised. The code lost readability as every "thing" was named "object". Debugging became difficult and the whole point of DRYing up the code was lost.
This was just one case. There are several of them where DRYing up resulted in complex, hard-to-debug code. So the question is, when do we stop DRYing up the code? Because not every time you realize the code has lost simplicity (And often the code author never realizes the simplicity of code is lost). Also, if we have to choose between simplicity and DRYed code, what should choose, if at all there comes a situation where you could get only either of them.
From what I understand, if DRYing up code is causing loss of simplicity, we are doing something terribly wrong. I think, we should be DRYing up code that is repeated and has single responsibility. If the code responsibilities are different and/or the abstraction of entities cannot be named, we are not repeating code. The code pattern might be repeated but its a different code altogether with a responsibilty of its own. If DRYing is resulting into vague code, you are probably trying to DRY up code with different responsibilities that have a similar pattern which is not really a good practice. DRYing should enhance the simplicity, not suppress it.
If you are following REST, then yes, the controllers will be very similar and largely boilerplate. I agree with your manager that it's a problem.
It sounds though like he came up with a suboptimal solution. For a better one, check out Jose Valim's inherited_resources plugin that is being incorporated into Rails 3.
Readability and Maintainability are the two most important features of good code. Unfortunately, a compromise has to be made sometimes. It's a balance question and not everyone is going to agree.
Myself, I lean towards your point of view as well. I would rather have some apparent repetition if it means the code is easier to understand.
As for the 'debugging' problem, I am in the habit when I create such a 'base class' to include a supplementary field. This field is a simple string which identify the most derived class (and is thus passed from Constructor to Constructor). Then each message is going to print this field + the object id "realtype[id]" and everything is suddenly much easier to debug.
Now on to DRY.
There are two things to DRY:
building a hierarchy
using generic code
The first point should now be well understood. A hierarchy of class means a IS-A relationship. If two classes have similar behavior but are otherwise functionally unrelated, then they SHOULD NOT be part of the same hierarchy. It only confuses the poor maintainer and hurts readability.
The second point can be used much more often, especially with scripting languages. For the precedent example, I would argue that instead of having a hierarchy of classes, you could simply define generic methods that would take different classes (modeling different business) and treat them uniformly. This way you avoid repetition (DRY) yet you do not sacrifice readability (imho).
My 2 cts.
If someone every told me--with a straight face--that my code needed DRYing, I would probably take that as a sign that anything else they were going to do was going to be really far-fetched and for-the-sake-of-it.
That having been said, there is also a difference between simplicity in writing code (laziness) and simplicity in the code itself (elegance). I agree, though, that there is a balance. I had this situation myself one particular time (in PHP, but oh how it reminds me of your dilemma):
$checked = ($somevariable) ? "checked=\"checked\"" :"";
echo "<input type="radio" $disabled_checked />";
$checked = ($someothervariable) ? "checked=\"checked\"" :"";
echo "<input type="radio" $checked />";
This isn't even a very good example of what I was dealing with. Essentially, because it's a radio input, both inputs needed a some way of knowing which was to be bubbled in. I knew it had what your boss might call "wetness" issues, so I racked my head trying to come up with some solution that would be graceful and to the point. Finally I showed it to a senior developer and he said "No, it's all in order, it does what it needs to. It's only one extra line."
I felt a relief at being reminded that I was hurting my project more then helping by worrying over this, but at the same time, I'm still disappointed that he was so casual about a fundamental principle (as though it wasn't one of his, though I'm sure it is).
So while I agree, your manager probably was doing something just for the sake of doing it, it is only when we strive to come up with the better methods and approaches that we get better languages like Ruby and Python and cooler libraries like Jquery.
Basically, what if next week you suddenly had 70 things instead of 2? If your boss's objects make that a snap, he was right. If it's the same amount of trouble (in the code or in the execution), he was wrong. But that doesn't mean there isn't a better answer then keeping it simple because it's only a couple of things.
The aim of the DRY principle is to help increase the "quality" of the code.
If the changes aren't improving the quality of the code, it is time to stop.
The ability to judge this comes with experience. As requirements change, the most appropriate way to refactor the code also changes, so it's impossible to have everything ideal - at least you need to freeze the requirements first.
Minimising the size of the code should generally not be a consideration in the quality unless you are codegolfing, so don't DRY when the only purpose is to reduce the size of the code.
Complicated tricks can do more harm than good.
A key reason for applying DRY to improve maintainability is to ensure that when a code change is necessary, that change only needs to be made in one place, thus avoiding the risk that it doesn't get changed everywhere that needs it.
But I'm not telling the whole story:
This interview with Dave Thomas has DT saying:
DRY says that every piece of system
knowledge should have one
authoritative, unambiguous
representation.
The first time I saw "DRY" was in The Pragmatic Programmer so I'm inclined to go with Dave on this.
There's another article worth reading here
But DRY is a principle, not a rule: the better we understand the principle, the more able we should be to recognise situations where it should be applied.
(And finally, I think I'd want a little more than "more-or-less the same" before I started "DRY"ing that code: if I could see a clear way in which the two things might diverge in the future then I'd be inclined to leave them alone).
For me, duplicated code is a smell that can have multiple origins:
Missing variables (introduce variable).
Missing methods (push expression into a method).
Feature envy (push behaviour down into the envied class).
Over-generalization (break up generic class into specific concrete classes).
Insufficient abstraction (push attributes and behaviour down into new class).
This list is probably incomplete. Consider it a starting point.
When you find duplication, think about what problem it's symptomatic of. Then take a stab at addressing that problem. When you're done, consider the readability of the new code. If it has deteriorated, you may be in one of these positions:
You misidentified the problem at the root of the duplication (revert, rethink, try again).
The duplication is a necessary trade-off (revert your change and live with it).
Your software is necessarily complex (commit your change and live with it).
Consider posting example code along with questions like this, if possible. They provide something concrete to work around. And remember, a lot of this stuff is very subjective.
If you're writing something by yourself, whether to practice, solve a personal problem, or just for entertainment, is it ok, once in a while, to have a public field? Maybe?
Let me give you an analogy.
I come from a part of the world where English is not the primary language. But it’s necessary for all things in life.
During one of those usual days during my pre-teen years I said something very funny in English. Then my Dad said, “Son, think in English. Then you’ll get fluent”
I think it applies perfectly to this situation.
Think,try and question best practices in your playground. You will soon realize what’s best for what.Why are properties needed in the first place. Why should this be public? Why should I not call a virtual member from the constructor? Let me try using "new" modifier for a method call. What happens when I write 10 nested levels of if-then-else and try reading it again after 10 days. Why the heck should I use a factory pattern for a simple project. Et cetera.
And then you’ll realize without shooting at your foot, why design patterns are patterns...
I think it's reasonable if you're consciously throwing the code away afterwards. In particular, if you're experimenting with something completely different, taking shortcuts makes sense. Just don't let it lead to habits which cross over into "real" code.
Violating general principles is always "ok"! It is not an error to violate a principle but it is a trade off. The cost of not writing clean code will be higher the longer your software will survive. My take on this is: If in doubt make it clean!
Of course it's OK. It's your code, you can do whatever you want with it. Personally, I try to stick to good practice also in my private code, just to make it a natural habit so I don't have to think about it.
The short answer is yes, if you believe that you're gaining a lot by making things public instead of private with accessors you are welcome to do so. Consistency, I think, is the biggest thing to keep in mind. For instance, don't make some variables straight public, and some not. Do the same across the board if you break with best practices. It comes back to a trade-off. Almost no-one follows many of the IEEE specs for how Software Engineering should be executed and documented because the overhead is far too great, and it can get unmanageable. The same is true for personal, light-weight programming. It's okay to do something quick and dirty, just do not get used to it.
Public members are acceptable in the Data Transfer Object design patter:
Typically, the members in the Transfer Object are defined as public, thus eliminating the need for get and set methods.
One of the key advantages of OOP is for scaling and maintainability. By encapsulating code, one can hide the implementation. This means other programmers don't have to know the implementation, and can't change your object's internal state. If you language doesn't support properties, you end up with a lot of code which obfuscates and bloats your project. If the code doesn't need to be worked on by multiple programmers, you aren't producing a reusable component, and YOU are the maintenance programmer, then code in whatever manner allows you to get things done.
Does a maid need to make his/her own bed in the morning in order to practice properly making a bed?
Side note: it also depends on the language:
In Scala, according to the Uniform Access Principle, clients read and write field values as if they are publicly accessible, even though in some case they are actually calling methods. The maintainer of the class has the freedom to change the implementation without forcing users to make code changes.
Scala keeps field and method names in the same namespace.
Many languages, like Java, keep field and method names in separate namespaces.
However, these languages can’t support the uniform access principle as a result, unless they build in ad hoc support in their grammars or compilers.
So the real question is:
What service are you exposing (here by having a public field)?.
If the service (get/set a given type value) makes sense for your API, then the "shortcut" is legitimate.
As long as you encapsulate that field eventually, is it ok because you made the shortcut for the "right" reason (API and service exposure), versus the "wrong" reason (quick ad-hoc access).
A good unit test (thinking like the user of your API) can help you check if that field should be accessed directly or if it is only useful for internal development of other classes within your program.
Here's my take on it:
I'd advise avoiding public fields. They have a nasty habit of biting you later on because you can't control them. (The word you're looking for here is volatility.) Further, if you decide to change their internal implementation, you have to touch a lot more code.
Then again, that's what refactoring tools are for. If you have a decent refactoring tool, that's not nearly so difficult.
There is no silver bullet. I can't repeat this enough. If you have work to do, and you need to get it done in a hurry, writing one line of code instead of eight (as is the case in Visual Basic) is certainly faster.
Rules were meant to be broken. If a rule doesn't necessarily apply in your case, don't use it. Design patterns, coding guidelines, laws and best practices should not be treated as a straightjacket that requires you to needlessly complicate your code to the point where it is enormously complex and difficult to understand and maintain. Don't let someone force you into a practice just because it's popular or "standard" when it doesn't fit your requirements.
Again, this is a subjective opinion, and your mileage may vary.
I've read a lot about IoC and DI, but I'm not really convinced that you gain a lot by using them in most situations.
If you are writing code that needs pluggable components, then yes, I see the value. But if you are not, then I question whether changing a dependency from a class to an interface is really gaining you anything, other than more typing.
In some cases, I can see where IoC and DI help with mocking, but if you're not using Mocking, or TDD then what's the value? Is this a case of YAGNI?
I doubt you will have any hard data on it, so I will add some thoughts on it.
First, you don't use DI (or other SOLID principles) because it helps you do TDD. Its the other way around, you do TDD because it helps you with the design - which usually means you get code that follow those principles.
Discussing why to use interfaces is a different matter, see: https://stackoverflow.com/questions/667139/what-is-the-purpose-of-interfaces.
I will assume you agree that having your classes do many different things results in messy code. Thus, I am assuming you are already going for SRP.
Because you have different classes that do specific things, you need a way to relate them. If you relate them inside the classes (i.e. the constructors), you get plenty of code that uses specific versions of the classes. This means that making changes to the system will be hard.
You are going to need to change the system, that's a fact of software development. You can call YAGNI about not adding specific extra features, but not on that you won't be needing to change the system. In my case that's something really important as I do weekly sprints.
I use a DI framework where configuration is done through code. With a really small code configuration, you hook up lots of different relations. So, when you take away the discussion on interface vs. concrete classes, you are actually saving typing not the other way around. Also for the cases a concrete class is on the constructor, it hooks it up automatically (I don't have to configure) building the rest of the relations. It also allows me to control some objects life time, in particular I can configure an object to be a Singleton and it hands a single instance all the time.
Also note that just using these practices isn't more overhead. Using them for the first times, is what causes the overhead (because of the learning process + in some cases mind set change).
Bottom line: you ain't gonna need to put all those constructor calls all over the place to go faster.
The most significant gains from DI are not necessarily due to the use of interfaces. You do not actually need to use interfaces to have beneficial effects of dependency injection. If there's only one implementation you can probably inject that directly, and you can use a mix of classes and interfaces.
You're still getting loose coupling, and quite a few development environments you can introduce that interface with a few keypresses if needed.
Hard data on the value of loose coupling I cannot give, but it's been a vision in textbooks for as long as I can remember. Now it's real.
DI frameworks also give you some quite amazing features when it comes to hierarchical construction of large structures. Instead of looking for the leanest DI framework around, I'd recommend you look for a full-featured one. Less isn't always more, at least when it comes to learning about new ways of programming. Then you can go for less.
Apart from testing also the loose coupling is worth it.
I've worked on components for an embedded Java system, which had a fixed configuration of objects after startup (about 50 mostly different objects).
The first component was legacy code without dependency injection, and the subobjects where created all over the place. Now it happened several times that for some modification some code needed to talk to an object which was only available three constructors away. So what can you do but add another parameter to the constructor and pass it through, or even store it in a field to pass it on later. In the long run things became even more tangled than they already where.
The second component I developed from scratch, and used dependency injection (without knowing it at the time). That is, I had one factory which constructed all objects and injected then on a need to know basis. Adding another dependency was easy, just add it to the factory and the objects constructor (or add a setter to avoid loops). No unrelated code needed to be touched.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
The YAGNI "principle" states that you shouldn't focus on providing functionality before you needed as "you ain't gonna need it" anyway.
I usually tend to use common sense above any rule, no matter what but there are some times when I feel it is useful to over design or future proof something if you have good reasons, even if it's possible you'll never use it.
The actual case I have in my hands right now is more or less like this:
I've got an application that has to run over a simple proprietary communication protocol (OSI level 4). This protocol has a desirable set of characteristics (such as following NORM specification) which provide robustness to the application but which are not strictly required (UDP multicast could perform acceptable).
There's also the fact that the application is probably (but not surely) be used by other clients in the future which will not have access to the proprietary solution and, therefore, will need another solution. I know for a fact the probability of another client for the application is high.
So, what's your thinking? Should I just design for the proprietary protocol and leave the refactoring, interface extraction and so on to when I really need it or should I design now thinking for the (not so far) future?
Note: Just to be clear, I'm interested in hearing all kind of opinions to the general question (when to violate YAGNI) but I'd really like some advice or thoughts on my current dilemma :)
The reason YAGNI applies to code is that the cost of change is low. With good, well refactored code adding a feature later is normally cheap. This is different from say construction.
In the case of protocols, adding change later is usually not cheap. Old versions break, it can lead to communication failures, and an N^2 testing matrix as you have to test every version against every other version. Compare this with single codebases where new versions only have to work with themselves.
So in your case, for the protocol design, I wouldn't recommend YAGNI.
IMHO
I'd say go YAGNI first. Get it working without the NORM specification using 'the simplest thing that would work'.
Next compare if the cost of making the 'design changes' in the future is significantly greater than making the change now. Is your current solution reversible ? If you can easily make the change tomorrow or after a couple of months don't do it now. If you don't need to make an irreversible design decision now.. delay till the last responsible moment (so that you have more information to make a better decision)
To close if you know with a considerable degree of certainity that something is on the horizon and adding it later is going to be a pain, don't be an ostrich.. design for it.
e.g. I know that diagnostic logs would be needed before the product ships. Adding logging code after a month would be much more effort than adding it in today as I write each function... so this would be a case where I'd override YAGNI even though I dont need logs right now.
See-also: T. & M. Poppendieck's Lean books are better at explaining the dilemma of bullet#2 above.
Structuring your program well (abstraction, etc) isn't something that YAGNI applies to. You always want to structure your code well.
Just to clarify, I think your current predicament is due to over application of YAGNI. Structuring your code in such a way that you have a reusable library for using this protocol is just good programming practice. YAGNI does not apply.
I think that YAGNI could be inappropriate when you want to learn something :) YAGNI is good for the professionals, but not for students. When you want to learn you'll always need it.
I think it's pretty simple and obvious:
Violate YAGNI when you know that, in full certainty, You Are Going To Need It
I wouldn't worry. The fact that you aware of "YAGNI" means you are already thinking pragmatically.
I'd say, regardless of anything posted here, you are statistically more likely to come up with better code than someone who isn't analysing their practices in the same way.
I agree with Gishu and Nick.
Designing part of a protocol later often leads to thoughts like "damn, I should have done this that way, now I have to use this ugly workaround"
But it also depends on who will interface with this protocol.
If your control both ends, and that they will change of version at the same time, you can always refactor the protocol later as you would with a normal code interface.
About doing the extra protocol features implementation later, I found that implementing the protocol helps a lot to validate its design, so you may at least want to do a simple out-of-production code sample to test it, if you need the design to be official.
There are some cases where it makes sense to go against the YAGNI intuition.
Here are a few:
Following programming conventions. Especially base class and interface contracts. For example, if a base class you inherit provides a GetHashCode and an Equals method, overriding Equals but not GetHashCode breaks platform-documented rules developers are supposed to follow when they override Equals. This convention should be followed even if you find that GetHashCode would not actually be called. Not overriding GetHashCode is a bug even if there is no current way to provoke it (other than a contrived test). A future version of the platform might introduce calls to GetHashCode. Or, another programmer who has looked at documentation (in this example, the platform documentation for the base class you are inheriting) might rightfully expect that your code adheres without examining your code. Another way of thinking about this is that all code and applicable documentation must be consistent, even with documentation written by others such as that provided by the platform vendor.
Supporting customization. Particularly by external developers who will not be modifying your source code. You must figure out and implement suitable extension points in your code so that these developers can implement all kinds of addon functionality that never crossed your mind. Unfortunately, it is par for the course that you will add some extensibility features that few if any external developers ultimately use. (If it is possible to discuss the extensibility requirements with all of the external developers ahead of time or use frequent development/release cycles, great, but this is not feasible for all projects.)
Assertions, debug checks, failsafes, etc. Such code is not actually needed for your application to work correctly, but it will help make sure that your code works properly now and in the future when revisions are made.