Rob Kraft's Software Development Blog

Software Development Insights

Disable Right-Click Menus in Microsoft Access But Keep the Copy/Paste Menus

Posted by robkraft on February 26, 2021

I hope someone comments on this article to tell me that there is a better solution. I was unable to find it. The application requirements were:

  • Hide the right-click menus on the form and report title bars (shown below) to prevent users from entering “Design View”
  • Keep the right-click menus with textbox controls (shown below) to allow users to use it for Copy and Paste.

Microsoft Access has an option in the Current Database tab of Options to disable all the right-click menus and that option is called “Allow Default Shortcut Menus”. However, when I unchecked that, I could not figure out a way to enable the right-click menus solely within textbox controls to allow Copy and Paste.

My solution is to keep this option (“Allow Default Shortcut Menus”) set to checked (true), then run some code as the users open the application in AutoExec to disable the right-click on the form and report headers. I found no documentation that clearly described how to disable the right-click on the form headers, but with much trial and error I found that if I disabled all CommandBars that did not have a name, I got the result I was looking for.

Private Sub DisableCommandMenus()
    For Each Item In CommandBars
        If Item.Enabled = True Then
            'The menus we want to disable are those that don't have names
            If Trim(Item.Name) = "" Then
                Item.Enabled = False
            End If
        End If
    Next
End Sub

The code above disables menus that don’t have names. It is possible that I will need to disable more of the CommandBars, but at the moment my solution appears to be complete.

Do you know of a better solution to this?

Posted in Access, Coding | Leave a Comment »

Programmatically Invoke Microsoft Access SpellChecker using VBA – acCmdSpelling

Posted by robkraft on February 25, 2021

Microsoft Access has a built-in ability to run a spellcheck on your controls. It works best to call the VBA code to perform the spellcheck from the Exit method of the controls. I recommend calling DoCmd.SetWarnings to False prior to running the spellcheck because this will suppress “The spelling check is complete” message box from showing if there are not spelling errors in the control.

Private Sub Text5_Exit(Cancel As Integer)
    MySpellCheck Me!Text5
End Sub

Private Sub Text8_Exit(Cancel As Integer)
    MySpellCheck Me!Text8
End Sub

Private Sub MySpellCheck(ctl As Control)
    DoCmd.SetWarnings False
    With ctl
        .SetFocus
        If Len(.Value) > 0 Then
            .SelStart = 1
            .SelLength = Len(.Value)
            DoCmd.RunCommand acCmdSpelling
            .SelLength = 0
        End If
    End With
    DoCmd.SetWarnings True
End Sub

Posted in Access, Coding | Leave a Comment »

Trello Removes Feature for Creating Personal Board – A Setback From Their Atlassian Acquisition

Posted by robkraft on February 10, 2021

I discovered today that Trello no longer supports personal boards.  When you create a new board you need to create it within a Team first (teams will be renamed workspaces).

You can get around this by just creating your own team, perhaps name your team Personal.  But I see this as the first of many steps where Atlassian is re-purposing a popular tool for personal time management into a product optimized for users of other Atlassian products.  It is sad when a good product is acquired by another company then altered for different end users.

As I suspect more changes in the future, probably even pricing related, I am hunting for alternatives to Trello now.  There is a good list of some in this article:  The 17 Best Trello Alternatives in 2021 (In-Depth Comparisons) (kinsta.com)

Asana and Wrike are a few good alternatives.

Here is Atlassian trying to put a positive speed on their product degradation: Why can’t I create a board outside of a team anymore? – Trello Help

Posted in Project Management, Web Sites | Leave a Comment »

Track Changes to Access Form Properties in Source Control (Github)

Posted by robkraft on January 30, 2021

Last month I blogged about some code I borrowed and changed and used to track changes to code in Access databases in Github. Since then I have also added code to extract Access Queries along with Properties on Forms and Reports to track all of those in Github also. As maintainers of Access databases know, it is often difficult to identify what changes you intentionally or unintentionally made that may be causing the app to no longer behave as it once did. The code below helps.

I put this code in a github repo, so you can see the current version there: AccessUtilities/TrackAccessChanges.bas at main · RobKraft/AccessUtilities (github.com)

But you can also just copy and paste the code below and stick it in a .bas file and run it.

Run this code to export Form Properties, Report Properties, code from Forms, modules, and reports, along with query sources to external files. Then you can check the files into a source control repo. Do this daily and it gives you a good way to keep track of all the changes you made to your access database over time.

Note that this does not include ALL form and report properties, but you can change the code below if you want ALL of them.

Option Compare Database
    Dim debuggin As Boolean
    Dim filepath As String

Sub GatherInfo()
    debuggin = False
    filepath = CurrentProject.Path & "\"
    
    ExportAllCode
    robListAllFormProps
    robListAllReportProps
    robListAllQuerySQL
End Sub
Sub robListAllReportProps()
    Dim rpt As Report
    Dim reportIsLoaded As Boolean
    Dim outputThisProp As Boolean
    
    On Error Resume Next
    
    For Each rptHolder In Application.CurrentProject.AllReports
        reportIsLoaded = False
        For Each aLoadedReport In Application.Reports
            If aLoadedReport.Name = rptHolder.Name Then
                reportIsLoaded = True
            End If
        Next aLoadedReport
        
        If reportIsLoaded = False Then
            DoCmd.OpenReport rptHolder.Name, acViewDesign, , , acHidden
            If Err.Number <> 0 Then
                If debuggin Then
                    Debug.Print "Unable to analyze report: " & rptHolder.Name & " probably because of needing a specific printer. " & Err.Description
                Else
                    Print #1, "Unable to analyze report: " & rptHolder.Name & " probably because of needing a specific printer. " & Err.Description
                End If
            End If
        End If
        
        Set rpt = Application.Reports(rptHolder.Name)
        If debuggin Then
            Debug.Print rpt.Name
            Debug.Print "RecordSource = " & Trim(rpt.RecordSource)
            Debug.Print "Filter = " & Trim(rpt.Filter)
            ProcessFormOrReportMethods rpt.Properties
            Debug.Print ""
        Else
            Open filepath & "REPORTPROPSfor_" & rpt.Name & ".txt" For Output As #1
            Print #1, "RecordSource = " & Trim(rpt.RecordSource)
            Print #1, "Filter = " & Trim(rpt.Filter)
            ProcessFormOrReportMethods rpt.Properties
            Print #1, ""
        End If
        
        ProcessControls rpt.controls
        
        DoCmd.Close acReport, rpt.Name, acSaveNo
    
        If debuggin Then
        Else
            Close #1
        End If
    Next rptHolder
End Sub

Sub robListAllFormProps()
'https://docs.microsoft.com/en-us/office/vba/api/access.accontroltype
    Dim frm As Form
    Dim formIsLoaded As Boolean
    Dim outputThisProp As Boolean
    
    For Each frmholder In Application.CurrentProject.AllForms
        formIsLoaded = False
        For Each aLoadedForm In Application.Forms
            If aLoadedForm.Name = frmholder.Name Then
                formIsLoaded = True
            End If
        Next aLoadedForm
        
        If formIsLoaded = False Then
            DoCmd.OpenForm frmholder.Name, acDesign, , , acFormReadOnly, acHidden
        End If
        
        Set frm = Application.Forms(frmholder.Name)
        
        If debuggin Then
            Debug.Print frm.Name
            Debug.Print "RecordSource = " & Trim(frm.RecordSource)
            Debug.Print "Filter = " & Trim(frm.Filter)
            ProcessFormOrReportMethods frm.Properties
            Debug.Print ""
        Else
            Open filepath & "FORMPROPSfor_" & frm.Name & ".txt" For Output As #1
            Print #1, "RecordSource = " & Trim(frm.RecordSource)
            Print #1, "Filter = " & Trim(frm.Filter)
            ProcessFormOrReportMethods frm.Properties
            Print #1, ""
        End If

        ProcessControls frm.controls
        
        
        DoCmd.Close acForm, frm.Name, acSaveNo
    
        If debuggin Then
        Else
            Close #1
        End If
    Next frmholder
    
    

End Sub
Private Sub robListAllQuerySQL()
    For Each qryd In Application.CurrentDb.QueryDefs
        If Left(qryd.Name, 1) <> "~" Then
            Open filepath & "QUERY_" & qryd.Name & ".qry" For Output As #1
            Print #1, Trim(qryd.SQL)
            Close #1
        End If
    Next qryd
End Sub
Private Sub ProcessFormOrReportMethods(ctl As Properties)
    For Each prp In ctl
        outputThisProp = False
        If Left(prp.Name, 2) = "On" Then
                If Trim(prp.Value) <> "" Then
                    outputThisProp = True
                End If
            End If
            If (prp.Name = "BeforeUpdate" Or prp.Name = "AfterUpdate") Then
                If Trim(prp.Value) <> "" Then
                    outputThisProp = True
                End If
            End If
            If outputThisProp = True Then
                If debuggin Then
                    Debug.Print prp.Name & " " & Trim(prp.Value)
                Else
                    Print #1, prp.Name & " " & Trim(prp.Value)
                End If
            End If
        'End If
    Next prp
End Sub
Private Sub ProcessControls(controls As controls)
        For Each ctl In controls
            If ctl.ControlType <> acLabel And ctl.ControlType <> acRectangle And ctl.ControlType <> acPage And ctl.ControlType <> acLine _
                And ctl.ControlType <> acObjectFrame And ctl.ControlType <> acPageBreak And ctl.ControlType <> acTabCtl _
                And ctl.ControlType <> acCommandButton Then
                If debuggin Then
                    Debug.Print TypeName(ctl) & " - Name = " & ctl.Properties("Name")
                Else
                    Print #1, TypeName(ctl) & " - " & ctl.Properties("Name")
                End If
        
                For Each prp In ctl.Properties
                    outputThisProp = False
                    If prp.Name = "LabelName" Or prp.Name = "Text" Or prp.Name = "SelText" Or prp.Name = "SelStart" Or prp.Name = "SelLength" Or prp.Name = "ListCount" Or prp.Name = "ListIndex" Then
                    Else
                        If ctl.ControlType = acTextBox Then
                            If prp.Name = "ControlSource" Or prp.Name = "DefaultValue" Then
                                outputThisProp = True
                            End If
                        ElseIf ctl.ControlType = acCheckBox Then
                            If prp.Name = "ControlSource" Or prp.Name = "DefaultValue" Then
                                outputThisProp = True
                            End If
                        ElseIf ctl.ControlType = acListBox Then
                            If prp.Name = "ControlSource" Or prp.Name = "ColumnCount" Or prp.Name = "RowSource" Or prp.Name = "RowSourceType" Or prp.Name = "BoundColumn" Then
                                outputThisProp = True
                            End If
                        ElseIf ctl.ControlType = acComboBox Then
                            If prp.Name = "ControlSource" Or prp.Name = "ColumnCount" Or prp.Name = "RowSource" Or prp.Name = "RowSourceType" Or prp.Name = "BoundColumn" Then
                                outputThisProp = True
                            End If
                        ElseIf ctl.ControlType = acOptionGroup Or ctl.ControlType = acOptionButton Then
                            If prp.Name = "ControlSource" Then
                                outputThisProp = True
                            End If
                        ElseIf ctl.ControlType = acSubform Or ctl.ControlType = acToggleButton Then
                            If prp.Name = "SourceObject" Or Left(prp.Name, 4) = "Link" Then
                                outputThisProp = True
                            End If
                        Else
                            If ctl.ControlType = acRectangle Or ctl.ControlType = acPage Or ctl.ControlType = acLine Or ctl.ControlType = acObjectFrame Or ctl.ControlType = acPageBreak Or ctl.ControlType = acTabCtl Then
                            Else
                                outputThisProp = True
                            End If
                        End If
                        If Left(prp.Name, 2) = "On" Then
                            If Trim(prp.Value) <> "" Then
                                outputThisProp = True
                            End If
                        End If
                        If (prp.Name = "BeforeUpdate" Or prp.Name = "AfterUpdate") Then
                            If Trim(prp.Value) <> "" Then
                                outputThisProp = True
                            End If
                        End If
                        If outputThisProp = True Then
                            If debuggin Then
                                Debug.Print vbTab & prp.Name & " " & Trim(prp.Value)
                            Else
                                Print #1, vbTab & prp.Name & " " & Trim(prp.Value)
                            End If
                        End If
                    End If
                Next prp
            End If
        Next ctl
 
End Sub



Public Sub ExportAllCode()

    Dim c As Variant
    Dim Sfx As String
    Dim filen As String

    For Each c In Application.VBE.VBProjects(1).VBComponents
        Select Case c.Type
            Case 2 'vbext_ct_ClassModule, vbext_ct_Document
                Sfx = ".cls"
            Case 100 'vbext_ct_MSForm
                Sfx = ".frm"
            Case 1 'vbext_ct_StdModule
                Sfx = ".bas"
            Case Else
                Sfx = ""
        End Select
        
        filen = c.Name
        If (Left(filen, 18) = "report_rpt_company") Then
            If (Right(filen, 7) = "summary") Then
                filen = "report_rpt_company_YearlySales_Summary"
            End If
        End If
        If Sfx <> "" Then
            'If Left(c.Name, 16) = "form_frm_product" Then
            c.Export _
                FileName:=CurrentProject.Path & "\" & _
                filen & Sfx
            'End If
        End If
    Next c

End Sub

Posted in Access, Coding | 1 Comment »

Forcing dynamic content to render Client-Side on a Netlify Gridsome SSR Vue Site

Posted by robkraft on January 19, 2021

I inherited a site build on Netlify using Gridsome and Vue Server-Side Rendering (SSR).  The site is pretty good.  It loads very fast and the content is easily maintained by people that are not programmers.  However, we occasionally want to embed a form from another site, such as OpenForms.com, and that is challenging.  The idea behind Vue SSR is that the server will render and load ALL the content then provide it to the browser, and trying to run JavaScript on the client is challenging because traditional events used by SPA and web page JavaScript programmers don’t fire.

Furthermore, the Gridsome CMS uses Markdown for web page content, but the ability to place HTML and JavaScript in these pages is limited.  I battled for days to create a solution and am sharing how I made it work.  I’ll admit it may not be the best solution; please let me know if there is a better way; but this solution works robustly.

Step 1: In the Gridsome MarkDown page, add the anchor tag that links to your form as provided by OpenForms.  Something like this:

<a class="openforms-embed" href="https://us.openforms.com/Form/{your form ID here}">Click here to view form.</a>

I did not include the script tag to load the JavaScript here because it won’t load reliably here, so I load it elsewhere.

Step 2: On the Vue page that renders the MarkDown page from Step 1, add code in the mounted() and updated() events. (The console.log is not necessary, but I use it to help me understand what is going on.)

export default {
   mounted() {
     console.log("mounted basic: " + window.location.pathname);
    evalScripts()
    
  },
  updated() {
    console.log("updated basic: " + window.location.pathname);
    evalScripts()
  },

Step 3: The important piece is calling the evalScripts() method I wrote which is this:

function evalScripts() {
  //This SeamlessOpenForms is specific to USOpenForms to get an openform to render every time 
  //the page is refreshed or viewed.
  if (typeof(SeamlessOpenForms) != 'undefined')
  {
      SeamlessOpenForms.loadOpenForms();
  }
  else {
      const openforms = document.querySelectorAll(".openforms-embed");
      if (openforms.length>0)
      {
        console.log("missing SeamlessForms: " + window.location.pathname);
        const scriptPromise = new Promise((resolve, reject) => {
          var scriptElement = document.createElement('script');  
          document.body.appendChild(scriptElement);
          scriptElement.src = 'https://us.openforms.com/Scripts/embed-iframe.js';  
          scriptElement.onload = resolve;
          scriptElement.async = true;
        });
        scriptPromise.then(() => { SeamlessOpenForms.loadOpenForms();});
    }
  };
}

I will attempt to explain what I think is going on with USOpenForms and the code above.  First of all, this code is risky because I reviewed the JavaScript in https://us.openforms.com/Scripts/embed-iframe.js provided by OpenForms to figure out what to do here, and it is very possible that OpenForms will change their JavaScript and what I am doing here will no longer work (our fallback is to put this in an Iframe, but that causes two vertical scroll bars).

When the JavaScript file (embed-iframe.js) loads it executes and looks for any anchor tags in the DOM with a class of .openforms-embed.  If it finds an item with that tag, it uses the src property to pull in the form and render it within the current page.  However, if a user navigates first to another MarkDown page, based on the same Vue Page, the embed-iframe.js looks for those anchor tags on that MarkDown page and does not find them.  When the user navigates to the MarkDown page containing the anchor tag, the embed-iframe.js does not load and run to look for anchor tags because it already did so when the first MarkDown page for that Vue component loaded.

The script above, gets called by either the mounted() or updated() event, one of which will fire every time a MarkDown page is loaded or refreshed.  The script will render the OpenForm via SeamlessOpenForms.loadOpenForms() if the JavaScript to do so is already loaded, but if not it will dynamically load that JavaScript, then perform the render code (SeamlessOpenForms.loadOpenForms();)

FYI – Here is a simple example of loading the form in an IFrame, which is what we did initially in the MarkDown until I got the embedded form JavaScript to work.

<iframe height="600px" width="100%" style="border:none;" src="https://us.openforms.com/Form/{your form id}"></iframe>

The explanation:

In the Gridsome/Vue SSR architecture, some window/DOM events only fire when the first web page (the first MarkDown page) based on that Vue Page is loaded.  So if you have dozens of MarkDown files that all use the same Vue Page (such as BasicPage.Vue), some javascript methods and Vue events only run when the first MarkDown page based on the Vue Page is loaded.  But the mounted() or updated() events always fire when a MarkDown page is loaded, rendered, or refreshed.

Posted in Coding, Web Sites | Leave a Comment »

Type Mismatch Error in VBA for Microsoft Access Parameters Collection

Posted by robkraft on January 8, 2021

I ran into a strange problem today with an project that I took over where code in Microsoft Access VBA is being converted. The original database, with untouched source code, has the following lines of code to cycle over all of the parameters in a querydef. The code below has been working for 15 years:

Dim db As Database
Dim qdf As QueryDef
Dim prm As Parameter
Set db = CurrentDb
Set qdf = db.QueryDefs(strQuery)

For Each prm In qdf.Parameters
    prm.Value = Eval(prm.Name)
Next

However, our current Access database project (*.mdb) that has the exact same code, but we have modified “something” now gets a “Type Mismatch” error number 13 when we try to “For Each” over the results of qdf.Parameters.

If I open a watch on qdf.Parameters I can view the parameters without a problem. There is only one parameter and I even confirmed that all properties of the parameter are identical when they run in the old version of our Access database app.

I could not figure out why the code no longer works. Even the query that runs is identical. However, I was able to fix the code to work again for all cases by replacing the For Each block with the following:

Dim i As Integer
For i = 0 To qdf.Parameters.Count - 1
qdf.Parameters(i).Value = Eval(qdf.Parameters(i).Name)
Next i

Posted in Access, Coding | Leave a Comment »

Tracking Microsoft Access Code Changes in GitHub; And Searches with Visual Studio

Posted by robkraft on December 27, 2020

I will admit up front that this approach is not as awesome as the post title may make it sound, but I do find it to be very useful and fairly simple, just a little tedious.

As a consultant, I occasionally get the Access database project that needs some fixes. Of course we can often easily make copies of the .mdb or .accdb files regularly, but as a programmer I want more. I want to be able to see the history of my code changes over time so that I can identify where changes had unintended consequences. I do this by exporting all of the source code out of the Microsoft Access database into flat files, then importing the flat files into a git repo. I have a bit of code below that I use for this, which I found elsewhere on the Internet and modified a little.

I create a new module and place the code below in that module. Then I execute the subroutine. Following that I import or update the files in my git repo. The approach works well but the export does not include macros, and it does not include properties and events on forms and reports, nor table structures. Still, I have found it useful. I also use this approach to pull up all the files I exported in Microsoft Visual Studio which has search tools I like better than those built into the Access database.

I run the code below about once per day to track the latest changes I made, then apply the updates to the git repo.

Public Sub ExportAllCode()

    Dim c As Variant
    Dim Sfx As String

    For Each c In Application.VBE.VBProjects(1).VBComponents
        Select Case c.Type
            Case 2 'vbext_ct_ClassModule, vbext_ct_Document
                Sfx = ".cls"
            Case 100 'vbext_ct_MSForm
                Sfx = ".frm"
            Case 1 'vbext_ct_StdModule
                Sfx = ".bas"
            Case Else
                Sfx = ""
        End Select

        If Sfx <> "" Then
            c.Export _
                FileName:=CurrentProject.Path & "\" & _
                c.Name & Sfx
        End If
    Next c

End Sub

Posted in Access, Coding | Leave a Comment »

The Project Management Triangle Fails To Value Uncertainty

Posted by robkraft on June 22, 2020

triangle-3125882_640

Most students of Project Management are familiar with a decision making tool known as the Project Management Triangle.  It is also known as the Iron Triangle, or the Triple Constraint.  The concept provides a mechanism for the product owners to make decisions.

In a nutshell, the concept states that you may control two of the three project factors (Time, Cost, and Scope/Quality), but the third factor is mostly out of your control.  Therefore you can:

  • Have the project fast and within scope (all features desired), but then it may exceed the desired cost,
  • Have the project fast and within cost, but then you may not get all the features in scope,
  • Have the project within cost and within scope, but then you may not get it on time

The Project Management Triangle is used by project managers in many industries, but in software development another factor often needs to be considered when making project decisions and that factor is uncertainty.

sign-575689_640

Uncertainty

Uncertainty doesn’t fit neatly in the Project Management Triangle.  Nor can you simply change the triangle into a square by adding uncertainty because uncertainty is not affected consistently with changes in the other three factors.  A decision that increases uncertainty may increase the time, increase the cost, decrease the quality, or even do all three.  Conversely, a decision that increases uncertainty could have the opposite affect on any or all of the sides of the triangle.

Side Note: Quality as Part of Scope

I believe that scope includes the aspect of quality.  There are two types of software quality.  One is the richness of features as seen by the users of the software.  The other is the robustness, integrity, and ease of maintenance of the code, deployed solution and ecosystem the solution runs in.  Scope can be easily equated with the quality visible to the users of the software.   But the quality of the ecosystem and maintainability of the source code is not so easily identified by users of the software.  Even so, for purposes of this article, I think that both types of quality can be included within scope.

The problem with the introduction of uncertainty is that, by definition, the decision makers don’t know if the detrimental effects will come to pass; only that detrimental effects are more likely to happen when uncertainty is introduced.  Therefore decision makers must decide if the change that is intended to reduce cost, reduce time, or increase quality, is worth the uncertainty that comes with it.

risk-3576044_640

Proactively Reducing Uncertainty

Some decisions reduce uncertainty.  In fact, project managers sometimes deal with risks by creating a new project to explore the uncertainty and eliminate the risk it entails.  I hesitate to call out benefits of the waterfall methodology of development, but risk reduction might be a step more easily adopted in waterfall, as opposed to agile, because waterfall is more likely to identify and consider the risks at the start.  This allows for developing a prototype to test the use of a new tool, component, or framework, in order to understand it better and reduce the uncertainties.  For Example, NASA reduces risks to its human astronauts by first launching unmanned rockets instead space.

Acknowledging Uncertainty

In the world of software development, we often must choose the solution to a problem from a menu of options.  Some options increase quality but at the cost of time.  Others decrease time but at the risk of quality.  And some increase uncertainty, particularly when the option introduces a new technology, tool, or process.

When a project has a tight deadline, a team should be less willing to risk an option that introduces new technologies, and thus uncertainty.  Also, when a project has high visibility or life-endangering impact, a team should be less willing to risk an option that introduces new technologies, and thus uncertainty.

Here are some examples:

  • A team discovers a defect during product development.  The team can choose a quick fix, which is to log an error message to a local text file, or choose a more robust fix, which is to log the message to a cloud-based service.  The robust fix is more desirable but might cause failures for some clients that implement the product in unexpected environments.  Is the team willing to accept the risk of the robust fix, or should they implement the quick fix and take more time to test and prove the robust fix before providing it in a future release?
  • A team chooses a technology they have not used before.  They choose to use .Net Core instead of the .Net Framework.  They accept that there are uncertainties, which means risks, to successfully developing with the technology as well as deploying it and supporting it.  They develop successfully but when they deploy they discover .Net Core is not supported on the server they deploy to.  They realize they need to upgrade the OS on the servers in order to support their application, but the IT team won’t be able to perform the OS upgrade for several more months.  This may be acceptable, but if the team had a tight deadline for the project then perhaps this project was not a good candidate due to the uncertainties that came with the new framework.
  • A team chooses a stronger encryption technology for the next release of their software.  When the software update deploys to some desktops it fails and the end users are unable to use the product.  The failure occurs because the encryption algorithm, a new technology thus containing uncertainties, is not supported by Windows Vista, an operating system that some customers still use.  You can probably guess how I came up with some of these examples. 🙂
  • A team chooses to skip significant testing of the software in order to release on time, within budget, while providing the scope desired.  Most all members of software development teams can tell you that inadequate testing increases the risk that the software will encounter problems in production use.

In the course of my career we have often opted for one solution over another not because one solution would reduce cost, reduce development time, or improve quality.  Instead, the solution we chose was the one with the least risk.  This was usually the choice when our release deadline was near and we did not want to introduce uncertainties.

Mitigating Risks

A technique we use to reduce the negative impact of risks is to use the new technologies  in minor features in our applications.  For example, when deploying a new logging framework we used it only in one minor component.  When we created a framework for sending emails via SMTP we only used it in an optional feature.  As we grew confident with the new technologies and increased their robustness, we expanded their use across the application.

Here are a few good articles about the traditional benefits of the Project Management Triangle:

https://rapidbi.com/time-quality-cost-you-can-have-any-two/

https://en.wikipedia.org/wiki/Project_management_triangle

Posted in Coding, Process, Project Management | 1 Comment »

Programmer’s Dilemma – Should you fix bugs before adding new features?

Posted by robkraft on June 19, 2020

The Scenario:

The purchase order web app worked very well for the first four departments that used it, but when the art department tried to use it they experienced some problems.  A few features, printing and file uploads, did not work.  The art department liked the app despite the problems, but they felt they could be more productive if all the features worked for them.

browser-773215_640

Developers determined that differences in the way the Safari browser behaved on touch tablets caused the issues.  The other departments used the Chrome browser on desktop PCs.  The two developers estimated that it would take them a month to rewrite to app to work on Safari also.  However, the two developers had several new projects queued up to work on.

Should the developers fix the first app to work on Safari before starting work on the new projects?

Let me provide you some additional information for consideration.

The developers have six new projects queued up, each will take them about one month to complete, and each is each estimated to save the company $50,000 per month when completed.  Fixing the bugs in the first app to work better on Safari will make the art department more productive and save the company about $5,000 per month.

scale-2247165_640

Based on this information, you could reasonably conclude that the return on investment of completing the new projects is higher than fixing the problems on the first project.  Therefore the developers should complete the next six projects before considering fixing the first project.

 

But let me provide you even more information to consider.

What if the knowledge gained and the frameworks created fixing the first project could be re-used across the next six projects guaranteeing that all projects work on both Chrome and Safari?  And what if the next six projects are public facing and therefore likely to be used by people using IPhones with Safari browsers via a touch interface?  The developers might be able to create the future apps more quickly and with higher quality.

mathematics-757566_640

Additional information may make decisions more difficult, but these are examples of the challenging decisions we face in software development.  Many variables contribute to our decision and most variables can’t easily be measured in dollars and cents.

much-3890920_640

I once attended a meeting with several customers of our software application.  We had a list of projects we could work on.  Some projects were for adding new features and others for fixing problems.  I’ll never forget what one business owner said, after another business owner had just argued that some of the problems in the software were costing them money and needed to be fixed first.  He said:

“I think of my company like a bucket with holes.  It has water, in the form money, pouring into it from the spigot, but it leaking.  We can spend time fixing the holes, or we can run the water faster.  Which way will get the bucket full the fastest?”

More succinctly, “Which course of action provides the most value?”.  That is the question we should be asking ourselves with every decision, every day.

Side note:

Shouldn’t we always fix bugs before adding new features?  Some development teams follow this philosophy but each team should decide for themselves if that is the right philosophy for them.  Fixing bugs before starting new features is more valuable for features that have not yet been released than when addressing bugs after they have been released.

Posted in Coding, Process, Project Management | Leave a Comment »

Programmer’s Parable – Gathering Requirements Up Front

Posted by robkraft on June 12, 2020

 

conversation-1262311_640The business owner asked the software developers to build a new web application for a single client.  She said that the web app needed to be really fast.  She told the developers this could be the first version of a new product that the company would build upon for years to come.

The developers were excited to work on something new.  They wanted to put their Agile skills to use and see if they could deliver the product within the expected two months while hitting their quality and performance goals.  They wondered what the product owner had in mind for the future beyond the first two months, but she was not readily available so they figured they would follow the Agile practice of focusing on one thing and getting it completely done before thinking about future features.

The team built a great site on time and within budget.  They cached as much data as possible to make it really fast.  The business owner was pleased.  The customers was pleased.  The developers were pleased because they had made everyone happy.  But the cloud IT manager was not pleased.  He noticed the price charged to the client barely covered the hosting cost of running the application in the cloud.

dancing-156041_640

At the party to celebrate the success of the web site, the cloud IT manager asked the business owner if she had a plan to reduce the hosting costs so that the return on investment (ROI) could be earned more quickly.  She laughed and said, “Of course I have a plan to improve ROI, but it is not by reducing hosting costs.  Now that we have one client on board, I’m going to have the team add dozens more clients to the same web site.  The incremental cost of adding each new client should be trivial, just like it is with our other products.  Once we have more clients, the hosting costs will seem trivial!”  She walked away to talk to someone else without recognizing the doomed expression upon the faces of the developers that overheard the conversation.

The Scrum Master saw the developers looking sad.  He asked them why they looked sad.  A developer replied, “She said she wants us to add more clients to the application.”, to which the Scrum Master replied, “Yes, I know.  It is going to be the top priority in our queue for the next iteration.  But why does that make you sad?”  The developer took a deep breath and said, “Because the only reason our web app is so fast is because we cache so much data on the server to avoid database calls.  The data cached is unique for the client.  We didn’t know that we would be expected to support multiple clients on the same web app.  We are going to have to redesign the entire app to support this.  Plus we don’t know if we can make it as fast as it is now.  If we had planned for the next release when we were developing the first version, we would have been able to avoid the complete rewrite that is now needed.”

checklist-150938_640

Analysis

Is it Agile if you engage in Big Design Up-Front (BDUF)?  The answer to that question is to point out that this is the wrong question.  In software, the question should never be, “Is it the Agile way?”.  The question should be, “What it is the best way?”.  Also, in this parable, the problem is lack of future requirements, not lack of future design.

In hindsight, knowing there would be future requirements, the team should have gathered some of those requirements up front.  Gathering requirements and creating designs for future releases is not “anti-Agile” and does not violate any rules of Agile development.  Getting requirements and thinking about design early is good.  You should only question “the amount of time” you spend gathering requirements and creating designs for future releases; not whether or not you do so at all.  If the time you spend on requirements and design for a future release moves beyond hours, beyond days, and into weeks, then you should become concerned that the time you are spending will become waste.  When that time is measured in a few hours, it is probably well worth it.

https://en.wikipedia.org/wiki/Big_Design_Up_Front

Posted in Code Design, Coding, Process, Project Management | Leave a Comment »