FURPSS with two SSes – The simplest choice of software quality taxonomy?

To design the software architecture for a system you must consider the software qualities it must have.

To think through that, you will want a checklist or framework.

An oldie-but-nearly-goody is FURPS, which listed qualities under five top-level headings:

  • Functionality
  • Usability
  • Reliability
  • Performance
  • Supportability
    but anyone working in software since the invention of the internet cannot ignore Security (which in FURPS was a sub-heading under functionality) as a top level concern, which gets us to:

FURPSS with two SSes

  • Functionality
  • Usability
  • Reliability
  • Performance
  • Supportability
  • Security

A 3-way split of design thinking

In practise the responsibility for achieving these qualities is using broken down as:

  • Functionality: this is what analysts and developers devote much of their attention to
  • Usability: this is what designers, together with those who care, pay attention to
  • Reliability, Performance, Supportability, Security: when these go wrong, everyone turns to the project architect, or principal engineer, or the tech lead.

I think this 3-way split matches the competencies required to deal with each area well. That is, being an expert in one of these areas does not make you an expert in the other two. To be a good, all-round lead in software requires some competence in all three. You must make the effort to learn some of the body of knowledge of all three areas, not just rely on picking stuff up as you go along.

When FURPSS is not enough

FURPSS is quiet minimal taxonomy and works very well for a lot (maybe most?) vertical application development, aka enterprise software.

But for bigger or more complex projects you need a bigger checklist. You need …

ISO/IEC 25010 (SQuaRE)

SQuaRE stands for Systems and software Quality Requirements and Evaluation. It defines about 40 qualities, under eight main quality headings:
• Functional suitability
• Performance efficiency
• Compatibility
• Usability
• Reliability
• Security
• Maintainability
• Portability

In addition, SQuaRE considers quality across stages of the product lifecycle, which FURPSS does not highlight, from early use and development to quality in use. For even a medium size project, maintainability matters already before you've finished building release 1; some projects never see the light of day because they become unbuildable.

Reading and learn some Square. But for most projects, it is more of a framework to pick from (which, exactly, of these 40 ways of looking at software quality are helpful to my project?). But it's a good way to review wether there is anything important that you might have missed.

All That is Old Is New Again. ISO 5055

SQuaRE has not altogether taken the world by storm. In fact, ISO5055 has subsequently been developed, which moves a little away from SQuarRE's focus on behaviour (you might think that is the right focus, but sometimes you just have to get technical) and back to the techy inside view of the system.
It's key headings are:

  • Security
  • Reliability
  • Performance Efficiency
  • Maintainability

Which is all but identical to the RPSS of FURRPSS.

But that is not the point of ISO5055. It's actual title is “Information technology — Software measurement — Software quality measurement — Automated source code quality measures” and it intends to be a way to automate the detection of quality failures before the system is deployed. Specifically it proposes to automate checking for the 138 issues listed in Mitre's Common Weakness Enumeration.

Conclusions for a project architect

  • FURPSS is good.
    It is enough for most application development. ISO5055 is something of a confirmation that RPSS are the four top-level quality requirements that are most wanted, alongside Functionality and Usability.
  • SQuaRE is good, for when you want to peruse, or use, a bigger checklist, or help for thinking about quality across stages of the lifecycle.

References

EntityFramework Core, Dependency Injection, and how to inject more dependencies when you can’t use the constructor

Perhaps having dependency injection in .Net for fifteen years has spoilt us a little. We expect any class to simply declare some dependency or other as a constructor parameter and by DI magic it all just works.

It doesn't work for everything though. Notably, it doesn't ‘just work’ in the dependency injection factories provided by Microsoft for EntityFramework. The DI factory .AddDbContext<T>(...) method expects your constructor to take a single DbContextOptions<T> options parameter. No other dependencies can be declared.

The injection technique offered instead by Entity Framework is to add IDbContextOptionsExtension instances to the DbContextOptions. This requires some boilerplate.

The Problem

You use microsoft.extensions.dependencyinjection. You use EntityFrameworkCore. You want to inject dependencies into a DbContext created by the DI factory. You can’t add constructor parameters. How to do it?

How To Create and Use an IDbContextOptionsExtension

As best I can see, the boilerplate for this is about five separate pieces of code. The final piece is a single line in your DI setup, in the same place you add out-of-the-box extensions like EnableDetailErrors:

services.AddDbContext<MyApplicationsDbContext>(
  (s,o) =>  {
               o.UseSqlServer(dbString)
                .EnableDetailedErrors()
                .EnableSensitiveDataLogging(isDevOrTest)
                // -----------------------------------------
                // 👇 add one line to inject your dependency
                .InjectMyThings( s.GetRequiredService<ThingToInject>() );
                // 👆 --------------------------------------
});

The Boilerplate

To make that one line work, you write four more pieces of code.

1. The DbContextOptionsBuilder extension method

/// <summary>
/// Boilerplate to make <see cref="DbOptionsMyInjectedThingExtension"/> available.
/// </summary>
public static class MyInjectedThingExtensionDbContextOptionsBuilderExtensions
{
    public static DbContextOptionsBuilder InjectMyThings(this DbContextOptionsBuilder builder, ThingToInject myInjectedThing)
    {
        (builder as IDbContextOptionsBuilderInfrastructure)
            .AddOrUpdateExtension(new DbOptionsMyInjectedThingExtension(myInjectedThing));
        return builder;
    }
}

2. Your custom IDbContextOptionsExtension class which holds the dependencies to inject

/// <summary>
/// Add MyInjectedThing to the <see cref="DbContextOptions"/>.
/// </summary>
public class DbOptionsMyInjectedThingExtension : IDbContextOptionsExtension
{
    ///<summary>This class carries the thing you want to inject</summary>
    public bool MyInjectedBool { get; }
    public ThingToInject EvenMoreInjectedThings { get; }

    public DbOptionsMyInjectedThingExtension(ThingToInject myInjectedThing)
    {
        MyInjectedBool = myInjectedThing.MyInjectedBool;
        EvenMoreInjectedThings = myInjectedThing;
        Info = new DbOptionsMyInjectedThingExtensionInfo(this);
    }
    public void ApplyServices(IServiceCollection services)
    {
        services.AddSingleton(this); 
    }
    public void Validate(IDbContextOptions options) { }
    public DbContextOptionsExtensionInfo Info { get; }
}

3. The EFCore MetaData

/// <summary>
/// Boilerplate to make <see cref="DbOptionsMyInjectedThingExtension"/> available.
/// </summary>
public class DbOptionsMyInjectedThingExtensionInfo : DbContextOptionsExtensionInfo
{
    /// <inheritdoc/>
    public DbOptionsMyInjectedThingExtensionInfo(DbOptionsMyInjectedThingExtension extension) : base(extension) { }

    /// <inheritdoc/>
    public override bool IsDatabaseProvider => false;

    /// <inheritdoc/>
    public override string LogFragment => "MyInjectedThing: {MyInjectedThing}";

    /// <inheritdoc/>
    public override int GetServiceProviderHashCode() => 0;

    /// <inheritdoc/>
    public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
        => (other.Extension as DbOptionsMyInjectedThingExtension)?.MyInjectedBool == (Extension as DbOptionsMyInjectedThingExtension)?.MyInjectedBool;

    /// <inheritdoc/>
    public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
    {
        debugInfo["MyInjectedThing:"] = $"MyInjectedThing={(Extension as DbOptionsMyInjectedThingExtension)}";
    }
}

4. Finally, your DbContext constructor

    public MyApplicationsDbContext(DbContextOptions<MyApplicationsDbContext> options) 
          : base(options)
    {
        MyInjectedThing = options.FindExtension<DbOptionsMyInjectedThingExtension>()?.EvenMoreInjectedThings;
        MyInjectedBool = EvenMoreInjectedThings?.MyInjectedBool ?? false ;
    }
    protected bool MyInjectedBool;
    protected ThingToInject MyInjectedThing;

A Proposal for Credit Assignment — Concerning the U.K. Goverment’s consultation on Copyright and Artificial Intelligence

The training of A.I. and machine learning models is, in the technical vocabulary of the field, a problem of credit assignment [1]. The entire achievement of a machine learning training algorithm lies in accurately tracing the myriad miniscule connections — assigning credit — to those features of the input data responsible for achieving each output target in its training data and targets.

It would be ironic then if, tasked with assigning credit to their inputs for their outputs, A.I. modellers should baulk and call that too daunting a challenge.

It is not necessary to achieve a great degree of accuracy, or timeliness or even consistency in assigning credit to content creators and IP holders whose property has been used to train models. A little effort to provide something that is half accurate most of the time would be a helpful start.

It is not necessary to invent a new compensation model, or a new process for registering the interests and contact details of IP owners. The music industry designed and implemented solutions for this problem over a century ago. Without the aid of A.I. Or a computer.

There is such a thing as legalized robbery. For instance, loan sharking used to be legal, but it was still robbery even when it was legal. We prefer to make such things illegal because it is wrong to let those with the will to do so to take advantage of people over whom they have an advantage of, for instance, physical strength.

In commerce, strength is largely financial, partly political. The bigger, better-connected company is immensely more powerful than the individual content creator. Just as with SLAPP cases and with libel laws, the legal battle is too one-sided to even contemplate.

The UK consultation on IP and A.I. provides a unique opportunity to develop and implement a far better vision. The relationship between A.I. developers and IP holders can and should be a positive, mutually beneficial, symbiotic relationship.

A.I. certainly depends, utterly, on IP creators. If the A.I. developer monetizes whilst the IP holder is left with nothing that would be parasitic and destructive[2], not symbiotic. If an algorithm and system is implemented to reasonably share with IP holders fair recompense for their works' contribution to A.I. output, the relationship could be symbiotic, mutually beneficial, and even a virtuous circle of increasing benefit to both. Not to mention the rest of us.

[1] https://www.google.com/search?q=back+propagation+as+an+algorithm+for+credit+assignment

[2] https://www.inet.ox.ac.uk/news/new-study-reveals-impact-of-chatgpt-on-public-knowledge-sharing