Software

You’re Spoiled for Choice: Dealing with Dependencies in Software Development

Make or buy? Decisions about whether to use frameworks or third-party software often lead to discussions about the wise use of time and money. Wir zeigen, warum es nicht immer sinnvoll ist, auf fertige Lösungen zu setzen und wann sich eine Eigenentwicklung lohnt.

Whether it’s large frameworks or libraries, or 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 About Costs

In most cases, nearly 100% of software development costs consist of personnel expenses. This creates a strong incentive to save 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 do you assess whether off-the-shelf software can save time? It’s 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 training period and a great deal of experience. In any case, weighing the pros and cons of using third-party software versus writing your own is no simple 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 required in the short term for training and integrating third-party software). 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 options?

The decision to develop software in-house or use off-the-shelf software is usually based on limited experience. Furthermore, it may not become clear until later whether there are functional differences that aren’t apparent at first glance. These differences only surface later in the implementation process—a problem that cannot be completely eliminated.

So, 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 simplify the decision.

Scope of the Solution

“It offers so much—we're definitely well-positioned for the future.”

The argument might go something like this 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 the functionality yourself? Do I really need a component like leftpad?

It makes sense to take a look at the scope of the solution you need. Sometimes there are no real arguments in favor of the more complex off-the-shelf solution, yet you still opt for it because of potential you’ll never fully utilize.

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 a third-party component has achieved is a key indicator of its future viability. Factors include:

  • Commitment, popularity (for open-source software)
  • The software has a stable business model through which the vendor generates revenue
  • Market acceptance

 

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

The effort required for integration 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 they are future-proof, since with in-house development, the company’s own development team knows the solution inside and out and can keep it stable.

Nothing is free

Neither is free, because keeping things up to date always requires some effort to integrate, even though automated version control in package managers and approaches like semantic versioning are used in an attempt to escape “dependency hell” (e.g., caused by numerous dependencies and dependency cascades).

A Real-World Example – Caliburn.Micro

An example from one of our software teams—which develops software using WPF and .NET—shows just how simple implementation can be. For this project, they used the Caliburn.Micro framework. 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 matching of the view and view model is done based on the names of the components.
  • When DevExpress was additionally integrated, 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 into the WPF pattern, as it was unclear which pattern was being used at any given point.

 

So, the first step was to assess the scope of the solution. The MVVM pattern itself is a smaller, isolated component. It can be easily separated and implemented independently, without any dependency on Caliburn.Micro. The integration and familiarization with the external framework required a similar amount of effort.

The implementation of our in-house development therefore involved:

  • Isolating the MVVM pattern mapping via filename conventions (instead of component names) and
  • integrating it into WPF

 

We can now implement our own ideas more easily and quickly, and the code is much clearer and leaner, since we’ve 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 makes it easier to resolve issues using existing solutions.

The fact that it doesn’t require much programming effort is demonstrated by the code we wrote ourselves, which reflects 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 this was the right decision. At that time, 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 development that is typically carried out in their spare time.

Caliburn Screenshot

Fazit

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

There is a great deal of leeway for these decisions; team members can 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 results in 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, however, you should always be aware that this isn’t a sure thing. Instead of simply integrating it and forgetting about it, a high level of care and maintenance is essential even with third-party software. 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 isn’t 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 decisions should be made with this in mind.

Your Journey with OHB Digital Services

Leverage space technology for your business. OHB Digital Services GmbH has been a reliable partner for secure and innovative IT solutions for many years. We are part of one of the most successful aerospace and technology companies in Europe. With our products and services, we support you in areas such as the digital transformation of your business processes along the value chain and with all security-related issues. Please feel free to contact us.

Recent magazine articles on Software

Software Code
Software
Remote Pair Programming During COVID-19
How can remote pair programming be implemented during the COVID-19 pandemic, when all employees are working remotely? Our experience so far shows that it works very well!
Learn more
social engineering32
IT-Sicherheit
What 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.
Learn more
RedTeaming32 1
IT-Sicherheit
What is red teaming, and who can benefit from it?
In this article, we explain the benefits of red teaming and show you which companies this specific type of penetration test is suitable for.
Learn more

Does this sound interesting to you and your company?

Then please contact us.