Software

You're spoiled for choice: Dealing with dependencies in software development

Make or buy? Decisions regarding the use of frameworks or third-party software often spark discussions about the wise use of time and money. We’ll explain why relying on off-the-shelf solutions isn’t always the best approach and when developing your own solution is worthwhile.

Whether it’s large frameworks or libraries, small tools, or collections of useful functions: it seems as though solutions already exist for every problem. Most of the time, these are available for free as open-source software (OSS). Sometimes they cost money, but these days the costs are often very low. An hour of a developer’s time is significantly more expensive. Third-party software can be quickly integrated thanks to package managers. This often pleases developers and project managers alike—after all, it saves us all time and money. So what’s the catch?

The fallacy regarding costs

In most cases, nearly 100% of software development costs consist of personnel expenses. This creates a strong incentive to save on developer time. The promise of a tool that reduces developer time is therefore an obvious one. And the conclusion that using off-the-shelf software to “buy in” parts of the development process is, in principle, correct, because only by reducing development effort can time—and thus money—be saved in the end.

But this is also where the problem lies. How exactly does one assess whether off-the-shelf software can save time? It is not the initial implementation, but rather the time spent troubleshooting issues and continuously maintaining the software that drives costs. These time expenditures depend primarily on whether the development team understands the software and is thoroughly familiar with the code. With third-party software, this is only possible after a long period of training and with considerable experience. In any case, weighing the pros and cons of using third-party software versus writing your own is no trivial matter, as the follow-up costs are very difficult to estimate.

There is often a tendency to overestimate the promises at the outset, since the decision is made based on incomplete data and the immediate positive effects outweigh the drawbacks (less time spent on training and integrating third-party software in the short term). Package managers like NuGet or npm have taken this principle to the extreme and have drastically simplified the process of finding and integrating third-party software.

So how do you weigh the pros and cons?

When deciding between developing software in-house or using off-the-shelf software, there is usually little empirical evidence to draw upon. Furthermore, it may not become clear until later whether there are functional differences that are not apparent at first glance. These differences only emerge later during implementation—a problem that cannot be completely eliminated.

So, if you want to make a good decision, you need factors that allow you to determine, even with limited knowledge, whether “make” or “buy” is the better choice. The following tips can help simplify the decision.

Scope of the solution

"It offers so much; we're definitely well-positioned for the future."

That’s roughly how the argument might go if you only need a small part of the third-party software. But what if that’s all you need? Wouldn’t it be easier to write that functionality yourself? Do I really need a component likeleftpad?

It makes sense to take a moment to consider the scope of the solution you need. Sometimes there are no real reasons to choose the more complex off-the-shelf solution, yet people still opt for it, even though they won’t be able to fully utilize its potential.

Future-proof

"External components are maintained and updated independently"

It is certainly true that third-party components often receive updates and security patches that enhance functionality and improve stability. The maturity of a third-party component is a key indicator of its future viability. Factors include:

  • Commitment, popularity (in open-source software)

  • Software has a stable business model that allows the manufacturer to make money

  • Market presence

These factors are easy to evaluate; for example, the star ratings on GitHub provide an indication of the software’s popularity. Market penetration or the question of the business model, however, can often only be assessed qualitatively.

The integration effort remains

Regardless of the factors mentioned above, some effort is always required for integration. With in-house development, this effort is already factored in. With third-party software, integration often involves gaining an understanding of the system through documentation and trial and error. This effort tends to be greater for more complex solutions. For smaller solutions, it may therefore make sense to develop them in-house to ensure future-proofing, since with in-house development, the company’s own development team knows the solution inside and out and can maintain its stability.

Nothing comes for free

Neither is free, because keeping things up to date always involves some effort to integrate changes, even though automated version control in package managers and approaches likesemantic versioningare used to try to avoid "dependency hell"(e.g., caused by numerous dependencies and cascading dependencies).

A real-world example – Caliburn.Micro

You can see just how simple implementation can be from an example involving one of our software teams, which develops software using WPF and .NET. For this, they used theCaliburn.Microframework. The primary reason was to utilize the MVVM pattern.

The decision was easy, because Caliburn.Micro is free and very easy to use and integrate. By limiting themselves to the MVVM pattern, the team had chosen only a small, isolated part that was important to them. Nevertheless, problems arose relatively early on that made development unnecessarily complicated:

  • In the MVVM pattern, as implemented in Caliburn.Micro, the view and view model are matched based on the names of the components.

  • With the additional integration of DevExpress, this pattern could not be used as is. Instead, the standard WPF pattern—consisting of XAML and code-behind—had to be used.

  • By using two patterns, the team kept falling back on the WPF pattern, since it wasn't clear which pattern should be used in which situation.

So, first we looked at thescopeof the solution. The MVVM pattern itself is a smaller, isolated component. It can be easily separated and implemented on its own, without any dependency on Caliburn.Micro. Integrating it and getting to grips with the external framework required a similar amount of effort.

The implementation of the in-house development therefore included:

  • Isolating MVVM pattern matching based on filename conventions (rather than component names) and

  • Integration into WPF

It is now easier and faster to implement our own ideas, and the code is much clearer and more streamlined, since we have implemented only the functions we actually need. Our framework is thus much easier to understand and therefore ready for any maintenance or expansion. The use of our company’s proprietary standard formats and structures makes it easier to work with the software internally. This means that problems can be resolved more easily using existing solutions.

The fact that it doesn't require much programming work is demonstrated by the code we wrote ourselves, which illustrates the benefits we hoped to achieve with Caliburn.Micro.

Code example

				
					/// <summary>
        /// Searches for View models and views that match the name convention and adds them to the resource dictionary
        /// </summary>
        /// <param name="typeNamespace"> The namespace in which will be searched for the view models</param>
        /// <param name="res">Reference to Dictionary in which the template should be stored</param>
        /// <param name="relTypes">Array of types that contains among other types, the view model types and view types</param>
        /// <returns>List that contains the imported templates</returns>
        public static List<String> GenerateTemplateDictionary(String typeNamespace, ResourceDictionary res, Type[] relTypes)
        {
            // get names of namespaces for VMs and Views by convention
            String vmNs = $@"{typeNamespace}.ViewModels";
            String viewNs = vmNs.Replace("Model", String.Empty);

            // getting types of VMs and Views from that namespaces
            var viewModelQuery = from t in relTypes
                          where t != null && t.IsClass && !t.IsAbstract && t.Namespace != null && t.Namespace.StartsWith(vmNs)
                          select t;

            var viewModelTypeList = viewModelQuery.Where(t => t != null).GroupBy(t => t.Name).Select(g => g.First()).ToDictionary(t => t.Name, t => t);

            var viewQuery = from t in relTypes
                            where t != null && t.IsClass && !t.IsAbstract && t.Namespace != null && t.Namespace.StartsWith(viewNs)
                            select t;
            var viewTypeList = viewQuery.Where(t => t != null).GroupBy(t => t.Name).Select(g => g.First()).ToDictionary(t => t.Name, t => t);

            var foundItems = new List<String>();

            // link the VM types to View types by convention into a datatemplate and add the datatemplate to app.resources
            foreach (var vmt in viewModelTypeList)
            {
                var viewKey = vmt.Key.Replace("Model", String.Empty);
                if (viewTypeList.ContainsKey(viewKey))
                {
                    var viewType = viewTypeList[viewKey];
                    var template = CreateTemplate(vmt.Value, viewType);
                    if (!res.Contains(template.DataTemplateKey ?? throw new InvalidOperationException()))
                    {
                        res.Add(template.DataTemplateKey ?? throw new InvalidOperationException(), template);
                        foundItems.Add($@"Found and add VM -> View: {viewType.FullName} -> {vmt.Value.FullName}");
                    }
                    else
                        foundItems.Add($@"Pair is already known VM -> View: {viewType.FullName} -> {vmt.Value.FullName}");
                }
            }

            return foundItems;
        }
        
        private static DataTemplate CreateTemplate(Type viewModelType, Type viewType)
        {
            // The only way to get around some problems with binding later is this way of creating datatemplate objects
            // see www.ikriv.com/dev/wpf/DataTemplateCreation/ 

            const String xamlTemplate = "<DataTemplate DataType=\"{{x:Type vm:{0}}}\"><v:{1} /></DataTemplate>";
            var xaml = String.Format(xamlTemplate, viewModelType.Name, viewType.Name);

            var context = new ParserContext { XamlTypeMapper = new XamlTypeMapper(new String[0]) };

            context.XamlTypeMapper.AddMappingProcessingInstruction("vm", viewModelType.Namespace ?? throw new InvalidOperationException(), viewModelType.Assembly.FullName);
            context.XamlTypeMapper.AddMappingProcessingInstruction("v", viewType.Namespace ?? throw new InvalidOperationException(), viewType.Assembly.FullName);
caliburnmicro.com
            context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
            context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
            context.XmlnsDictionary.Add("vm", "vm");
            context.XmlnsDictionary.Add("v", "v");

            var template = (DataTemplate)XamlReader.Parse(xaml, context);
            return template;
        }
				
			

It's incredibly easy to use

				
					DynamicVmTemplateFinder.GenerateTemplateDictionary(typeof(App), Current.Resources,  Assembly.GetExecutingAssembly().GetTypes())
				
			

The Dictionary handles the mapping by binding the View and ViewModel together.

It became clear by June 2020 at the latest that the decision had been the right one. At that point, the developer marked Caliburn.Micro as no longer maintained.

The reasons are often the same. There are only a few developers responsible for the project, and as their personal or professional circumstances change, they are often no longer able to continue working on the project in their spare time.

Caliburn Screenshot

Conclusion

This example shows that the decisions and considerations made in this case resulted in a successful investment. The freedom to make these decisions within the development team is a hallmark of working at OHB Digital Services.

There is considerable flexibility in making these decisions; team members are encouraged to contribute their own ideas, and the project manager does not dictate which type of library should be used. Because decisions are made as a team, this often leads to better solutions.

Over time, we've found that it often makes more sense to create our own solutions and develop a simple, small framework ourselves rather than using more complex third-party frameworks.

If you do decide to use a third-party library, you should always be aware that this is not a sure thing. Rather than simply integrating it and forgetting about it, third-party software also requires a high degree of care and maintenance. As the number of dependencies increases, it’s easy to lose sight of hidden incompatibilities. This can also lead to time-consuming bug fixes if the third-party software is not thoroughly understood. All of this must not be overlooked.

Ultimately, of course, the goal is to provide the best possible product for the customer, and that should be the basis for the decision.

Your journey with OHB Digital Services

Leverage space technology for your business. OHB Digital Services GmbH has been a trusted partner for secure and innovative IT solutions for many years. We are part of one of Europe’s most successful space and technology companies. With our products and services, we can help you digitize your business processes across the value chain and address all security-related issues.Feel free to contact us.

Recent magazine articles on the topic of software

social engineering32
IT Security
What exactly is social engineering?
From a seemingly harmless text message to a sophisticated phishing campaign—how attackers exploit employees’ vulnerabilities and trust to achieve their goals.
Read more
RedTeaming32 1
IT Security
What is red teaming, and who can benefit from it?
In this article, we explain the benefits of red teaming and highlight which companies this specific type of penetration test is best suited for.
Read more
VULNERABILITY ANALYSIS32
IT Security
Why should vulnerability analysis be a concern for small and medium-sized businesses as well?
More than half of all small and medium-sized enterprises in Germany have already fallen victim to a cyberattack; depending on the scale of the attack, the financial losses have run into the millions.
Read more

Does this sound interesting to you and your company?

Then please contact us.