I am, usually, not too shy of writing relatively long functions in the plugins. And I mean long – not 15 lines, not 30 lines. In a relatively complex plugin I would, occasionally, have 200+ lines in the Execute method. Here is an example:
Notice the line numbers. Even if I removed all the comments and empty lines from that code, I’d still be looking at around 200 lines of code.
This does not happen with simple plugins (since there is just not a lot of code which needs to be there in the first place), but this tends to happen with relatively complex ones.
So, of course, as a commonly accepted rule of thumb this is not good, and you can easily find examples where it is suggested to split long functions into smaller ones based on the functionality that can be reused or unit-tested independently.
Which, in turn, brings me to the question of whether that always applies to the plugins, or whether we are basically talking about personal preferences and it all depends?
Where plugins are somewhat special is that they are
- Inherently stateless
- Often developed to provide a piece of very specific business logic
The first item above just means that there is very little re-use of in-memory objects. Of course, we can create classes in the usual way, and we can create smaller methods in those classes to sort of split code into smaller pieces, but instances of those classes will always be short-lived. Which is very different from how they would normally be used in the actual applications where some objects would be created when the application starts, and they will be disposed of when the application stops. That might be minutes, hours, days, or even months. Yet different parts of the same application might be reusing those shared objects all the time.
In the plugins, on the other hand, everything is supposed to be short-lived, and, at most, we are looking at a couple of minutes (although, normally it should be just a fraction of a second).
This is not to mention that the same plugin can run on different servers, and, even when it’s running on the same server, there is no guarantee it won’t be a new instance of the same plugin.
That seems to render object-oriented approach somewhat useless in the plugins (other than, maybe, for “structuring”), but that does not necessarily renders functions useless.
Although, as far as functions go, imagine a plugin that needs to do a couple of queries, then cycle through the results, do something in the loops (different things in each loop), and, possibly, update related records in each loop.
Below is a relatively simple example with just a few conditions, and it already has 15 lines there.
The first 4 lines there are setting up the query. Although, what if we wanted to add related table to the query? And, maybe, more than one. That would be 3-4 lines per link, so we can easily end up with 10+ lines of code just to configure the query in those cases.
And there might be a few blocks like that in the same plugin.
We should also add the usual plugin initialization – that’s 4-6 lines more:
Although, the initialization part is, often, isolated into the PluginBase class which saves as a few lines at the beginning of the plugin.
But, still, with each plugin doing relatively unique queries, and applying relatively unique business logic to the specific operation/table, the functionality is, often, not that much reusable. Which means up until this point a lot of this seem to be personal preference and general “rule of thumbing”. Plus, maybe, personal preferences from the code readability standpoint; although (and that is an example of the personal preference), I’d often prefer longer code in such cases since I don’t have to jump back and forth when reading it / debugging it.
But what about the unit testing? FakeXrmEasy is the testing framework that probably comes to mind whenever we start thinking of writing unit tests for the plugins. The concept there is very interesting – we would create a fake context, which we would, then, use to execute our plugin against. This is a great idea, but, of course, it implies that we’d need to pre-create the context properly, to pre-populate it with the test data, etc. Which might result in quite a bit of work/maintenance, too.
Would it really matter for FakeXrmEasy how long our functions are in the plugins? Not that much, it seems, since FakeXrmEasy is treating plugins as black boxes, more or less. It does not care about the internal structure of the plugins being tested. It’s main purpose is to fake live context, as the name implies:)
In which case, just on the principle, and with the assumptions above (which is that the majority of the code in each individual plugin is, generally, not reusable across the project, and, also, that unit testing may have to be done on the plugin level rather than on the function level), does it matter how long that code is?
What do you think?
PS. This lead to a bit of a discussion
Here is what Daryl LaBar wrote in reply:
https://dotnetdust.blogspot.com/2021/09/Long-Functions-Are-Always-A-Code-Smell.html
And here is my reply to his reply above: