2

At the company I work for I want to improve the way we write software: designing our applications more SOLID. We've been in a new project for a few weeks and I've tried to apply some good practices in its design. For example, loose coupling by programming against interface instead of a concrete class, dependency injection / inversion of control, seperation of concerns etc. This is all meant as a springboard to test driven development in the (far) future.

In the application we're developing we need to calculate a score depending on a filled in survey by a user (a survey has categories which themselves have questions / answers). I've splitted the calculations in multiple classes and subsequent interfaces (calculating score of a question, category, survey, etc).

Also, in the front-end (ASP.NET Web Forms), I've splitted many parts of pages into seperate user controls. For example: a 'widget' which shows the culumative scores in the application or a widget for showing all files related to the application. These are all seperate user controls.

We're now a few weeks in the project and I find that some of my co-workers (especially ones older of age) are complaining about:

  • Not being able to find a implementation of a interface directly but needing to 'search' for the implementation
  • Needing more time to find the implementation of a particular markup on the front-page

They also don't see the point of splitting up seperate functionality into user controls because sometimes (usually) the user control is only used on one page. I, on the opposite, find it to work better since it keeps the code-behind of a page tight and lean.

My question is: Do I apply good practices the wrong way or does it simply take time to get used to it?

Note: Until very recently we developed software with tight coupling (e.g: static classes, never use interfaces, only use user control when necessary).

4
  • It's really a matter of what's the company's bottom line. Are you there to churn out short-lived applications as quickly as possible? Or are you there to build a system which will need to live for years, gradually adapting to changing requirements? If your case is the former, then you could probably get by without SOLID principles. I've been in that situation, and it's no fun for the dev, but it's not wrong per se. If your case is the latter, then SOLID will save you money and your sanity over the years.
    – MetaFight
    Commented Apr 26, 2014 at 11:57
  • Also, for what it's worth, it sounds like you're applying your SOLID principles correctly... though it's hard to tell without knowing more about the questionnaires and the business domain. It's possible that your coding is fine, but your domain model is flawed. We can't tell.
    – MetaFight
    Commented Apr 26, 2014 at 12:00
  • No, the applications a certainly meant to last long. And the requirements do change, yes. I just often find resistance against such changes in architecture or even TDD (arguments like 'I have not seen in working', 'Manual testing will always be required' or 'If the requirements change I need to change both test and production code'.
    – Sebazzz
    Commented Apr 26, 2014 at 12:05
  • 1
    A good litmus test I find with creating interfaces is if my interfaces should only have a single implementation, then they are a poor interface. Look out for things like IWidget and WidgetImpl. That suggests a split with no purpose other than to say you split it into an interface.
    – Brandon
    Commented Apr 26, 2014 at 16:30

2 Answers 2

5

Your first paragraph sounds like a case of cargo cult software engineering. Just because you are creating interfaces and using DI is not going to create a good program design. Just ask yourself: How many interface have only one concrete class as implementation? People having to look for implementation of an interface is clear indication there is something wrong. In many cases, interfaces might really be unneeded abstraction for your current business case and it just complicates the design.

One thing that raises red flag is this line:

This is all meant as a springboard to test driven development in the (far) future.

There is so much wrong with this kind of thinking. If you want to do TDD, do it from very beginning. First, having classes as interfaces doesn't make them testable. Especially if you are looking for tests that actually make sense and are not maintenance nightmare. TDD is meant as a way to ensure your code is divided in logical parts, that are logical to test. If, for example, you would have problems figuring how to test specific functionality, then it would be clear indication the code design is wrong. There is high chance, that even with your code, it will become problematic to create unit tests for it. TDD can also help against unneeded abstraction I mentioned above, because the class design will be only complex enough to fulfill all needed requirements, but not more. Here is nice example.

As for the mentality of others against TDD: Those are extremely common counter-arguments and stem from misunderstanding of TDD and there are even studies that say TDD improves quality of code, thus reducing defects and maintenance cost. All for 15-35% increase in initial development, which is negligible compared to time you save by reducing maintenance cost.

To sum it up. I would heavily recommend you to stop worrying about SOLID and learn something about OOP design and how to apply TDD.

3

A few things to keep in mind:

  • "Splitting" and "separating" code does a few things. Two of them: it disperses knowledge that once was concentrated (and could be read and understood in a single look or a few looks) and increases the vocabulary (sometimes, dramatically). With this in mind, don't "follow" "principles". One, they have very short limits. Two, many people take "following" as a good substitute for awareness and intelligence -- however, nothing beats looking honestly what is going on and analyzing it in all aspects. But by all means, test those limits if you can, make it clear the suggestion of exploring approaches to tackle existing problems (and have always a SOLID -- not the "principle" -- case for justifying the existence of these problems linked with the priorities of the project), and if your team is willing, there is a lot to learn doing so.

  • You mentioned classes and interfaces. I take you are using a language with poor instruments to do what you intent. This probably intensifies the previous bullet, specially regarding to the increase of "vocabulary". Its side effects include "people who once were fluent, are not fluent anymore", "it takes more effort to get fluent", and "it takes more effort to say what I mean". There is more to say about this instead of metaphors in quotes, but lets just conclude this naturally goes against human mood. And happy programmers are better at solving problems (warning: link to empirical research).

  • Both bullets of complains you mentioned are legit. And what you are aiming at (as far as finding a proper design and methodology) is also legit.

  • Also, food for thought: code identified with poor quality to one's eye is deceiving (specially regarding a foggy concept of "quality"). For instance, "local messes" (ie, confined to a procedure) which are uniform and easy to read (regardless if it irritates the eyes of a mind attached to a certain way of organizing things) imposes very little risk to the project, usually have 0 global influence in the design of the system given its local isolation (and, say, code is reentrant and pure / have no external side effects) and may be much more welcoming to changes. The same code refactored and separated, often times make it more difficult to read, understand and tackle, because the number of "moving parts" increased and it is not clear, from looking at a list of methods, what is the relationship of them, who is the entry point, how they cooperate, etc.

Regarding the last point, it's very common to see people going all the way with this separation (the reasons change with time but today it is done in the name of "making it easy to write unit tests"). At the same time, some of them frequently overlook the end result -- they notice the degenerated trash it became, they hesitate, you see their heads thinking "this is not..what I expected", but they insist on it; after all they followed "best practices", right? And if the result was pleasing with a previous refactored piece code, this refactored code should be pleasing too, right?....even though it is not. Frustrated, many start acting like a pendulum, and go to the opposite direction writing crap code with no thinking behind it and everything, but I digress.

So, concluding:

  • Choosing what to separate, how to separate and when to separate is considerably more important (specially, more important than any current notion of aesthetics. If the claim is "easy to change", it should be backed up by the risk of a certain section of code to be subjected to change, and how much or how little a design contribute to its overall economy $). In other words, this is not something that should always be done, and the result will always be good and profitable with no risk of making things worse. So constant evaluation and assessment of benefits, risks and trade offs precedes these practices.

  • If "separating" is silver (and an impure kind), "synthesizing" is gold.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.