Why I Dislike the @author Annotation

Here’s why I LOVE the @author annotation (in Javadoc comments): It makes me look really good having my name on tons of code. It shows that I am a high-output engineer.

Here’s why I HATE the @author annotation: It implies some sort of ownership of a method, class or package to the rest of the team.

When it comes to writing software, my experience has always been that smaller teams with good developers can get significantly more done than large teams with mediocre developers. (On this note, it may be a good idea to hire a single great developer at $150k rather than 3 junior programmers at $60k, but that’s a separate post).

The @author annotation can be very good to place in your javadoc comments so that others know who to turn to for questions about the code that was written. I’m not opposed to its use (in fact, I use it all the time). Its use, however, should not imply that others on the team are prohibited from modifying the code written by another programmer. On the contrary: Code is the responsibility of the entire team. All code!

There have been many occasions throughout my career wherein another developer said to me, “Hey Matt, can you write such-and-such a method so that I can get such-and-such?” An obvious example is in DAO classes. Another developer may be writing some controller code that requires some DAO method downstream. That developer should not find it necessary to ask the guy or gal who wrote the DAO class to implement a method. Write it yourself! Okay, there are certainly times when it may be appropriate to do so, but the main point is that we should make it clear that all code is the responsibility of everyone on the team.

Another example is when a defect is found. We’ve all made them… Writing code, to some extent, means writing defects. When a defect is found it is never appropriate to allow the rest of the team to be hung up because of it. The person discovering the defect, whether he or she wrote the defect or not, is free to correct it.

I’ll use myself as an example. One time I wrote a POJO class with a method like this:

getSTatus()

Oops! This is a pretty straightforward problem: That T should be lowercase. Because this was code that I checked in and my name appear as the @author, another team member pointed the typo out to me and asked me to correct the problem. This is certainly fair to do, but in the amount of time it took to call me over, show me the typo and send me to fix it, the other team member could have simply checked in a fix. That @author tag does not indicate that the @author is the only one allowed to modify any code.

A Bidirectional Add-To-List Mistake

Here’s a somewhat real-world example of a bad coding practice. When setting up bidirectional relationships, its important to remember that the add methods for adding to a list that results in a one-to-many and many-to-one relationship set the parent/child relationship in both directions. To make life simple, I like to provide a couple of different ways of adding to a one-to-many list. For example, say a database contains a “PEOPLE” table made of of Person objects, and each person has many Phones:

@Entity
public class Person {
    @Id
    @GeneratedValue
    private int id;

    private String name;
    private int age;

    @OneToMany(mappedBy = "person")
    @JoinColumn(name = "PERSON_ID")
    private List phones;
    ...
}
@Entity
public class Phone {
    @Id
    @GeneratedValue
    private int id;

    private String number;
    private String type;
    private boolean preferred;

    @ManyToOne(mappedBy = "person")
    @JoinColumn(name="PERSON_ID", nullable=false) 
    private Person person;  
    ...

    public Person getPerson() {
        return person;  
    }
}

Naturally, we need to enforce the bidirectional relation ship like so that adding a phone requires us to add a person (the PERSON_ID foreign key cannot be null!) and adding a phone via a person (person.addPhone(…)) requires that the a phone is added to the phones property and the phone object added to that list is assigned a parent person.

So in our Person class we need a method like this:

addPhone(Phone phone) {
    if (!phones.contains(phone) {
        phones.add(phone);  
        phone.setPerson(this);
    }
}

And in the Phone class we need:

setPerson(Person person) {
    if (this.person != person)
        this.person = person;
}

This is all fine and nice, but sometimes it is much easier to have a method that allows us to set all the child relationships at once. I like to offer both methods (i.e., my Person class would have both an addPhone and addPhoneList method). This isn’t at all uncommon. The problem is that where the phone owner is set may be confusing. Is it inside or outside the POJO?

It is tempting to leave the calling logic to do the work. For example:

List phones = new Phones();
for (some loop) {
    Phone = new phone;
    phones.add(phone);
    phone.setOwner(person);
}

person.addPhoneList(phones);

In our Person class we would then need a method like this:

addPhoneList(List phones) {
    this.phones = phones;
}

This assumes that whoever is writing the calling code understands the need to set the phone owner prior to persisting the person object. It also goes against our previous logic for adding a single phone to the list of phones for a person object. (I realize that I am ignoring the fact that my example method may leave some existing phone numbers orphaned or uni-directional, a separate issue).

A better approach to the addPhoneList method is this:

addPhoneList(List phones) {
    for (Phone phone : phones {
        phone.setPerson(this);
    }
}

By doing things this way we keep the work properly encapsulated to the Person class, and we don’t have to assume that the calling logic (and whoever writes it) set the owner for each phone in the list. Its just cleaner… As with nearly everything, there is a tradeoff between efficiency and bulletproof code. We are forced to loop through the list of phones twice in this scenario: Once when the phones are created and again when inserting a phone list to a Assuming we don’t have a massive list and are not making this call with extreme frequency, the overheard is really negligible.

This seems like a basic problem with an obvious solution, but I have seen code this written this say a number of times (where the add single phone method covers the bidirectional relationship but the add multiple method does not). Hibernate/JPA cannot magically figure out the parent/child relationship, so it is very important to enforce it and properly encapsulate responsibility within the persisted POJOs.

[Level Up: One-To-Many Associations]
[Level Up: Many-To-One Associations]

Is there ever a reason NOT to use an Artificial Primary Key?

I found this post on the subject of choosing a primary key. While Java Persistence Annotations allow us to use any field we want as a primary key (as long as it is naturally unique), is there a good reason to use anything that is not a surrogate/artificial primary key?

There are plenty of fine examples for natural primary keys: SKUs, usernames, email addresses, and so on. While these may work fine as a primary key insofar as they satisfy uniqueness requirements, there are some drawbacks, the biggest being the fact that uniqueness may not be guaranteed.

This post lists the reasons against using natural primary keys with 10 very good points:

  • Con 1: Primary key size – Surrogate keys generally don’t have problems with index size since they’re usually a single column of type int. That’s about as small as it gets.
  • Con 2: Foreign key size – They don’t have foreign key or foreign index size problems either for the same reason as Con 1.
  • Con 3: Asthetics – Well, it’s an eye of the beholder type thing, but they certainly don’t involve writing as much code as with compound natural keys.
  • Con 4 & 5: Optionality & Applicability – Surrogate keys have no problems with people or things not wanting to or not being able to provide the data.
  • Con 6: Uniqueness – They are 100% guaranteed to be unique. That’s a relief.
  • Con 7: Privacy – They have no privacy concerns should an unscrupulous person obtain them.
  • Con 8: Accidental Denormalization – You can’t accidentally denormalize non-business data.
  • Con 9: Cascading Updates – Surrogate keys don’t change, so no worries about how to cascade them on update.
  • Con 10: Varchar join speed – They’re generally integers, so they’re generally as fast to join over as you can get.

So while on the surface it may seem simple to use a seemingly unique field for a primary key (a username on a domain, for example), it can be disastrous later on. Con 6 above is the big one, but Con 7 is something people don’t seem to think about as much. We can enforce uniqueness on any field we want, be it a key field or not… That said, I really cannot think of a good reason to use a natural key (other than developer laziness, which is in fact the key reason why bad code tends to be written in the first place).

[Stack Overflow: Deciding between an artificial primary key and a natural key for a Products table]
[Rapid Application Development: Surrogate vs Natural Primary Keys – Data Modeling Mistake 2 of 10]
[Wikipedia: Surrogate Key]
[Wikipedia: Natural Key]

Valuable Unit Tests in a Software Medical Device, Part 9

I thought I was done, but here is yet another good reason to incorporate complex function automated testing: Validation of multiple Java runtime environments. Fabrizio Giudici proposes this as a solution for testing with Java 7, but we can always take it a step further, verifying multiple OS environments as well. Of course, this requires that we have those build environments available (easy enough, using virtual machines).

Where Do Hibernate Transactions Fit In?

I recently got into a discussion on where Hibernate transactions should be placed in the scope of DAO logic. Some people have a desire to begin and end a transaction inside a DAO method. This is really a question of unit of work, and not scope of the DAO.

Let’s say we have a single unit of work that involves updates to several tables. Within the scope of this unit of work we have to update table 1, table 2 and table 3. During the sequence of events table 1 updates just fine and then table 2 fails to update. Now, are we to move on and update table 3? What are we to rollback? Do we want to rollback both changes (the partial change to table 2 and the complete change to table 1 that is within the same unit of work)? (We probably do.)

But with our transactions being committed within every single DAO method we cannot; The previous transaction was already committed before we discovered an error.  So the ultimate answer is this: Placing transaction begin and end statements within each DAO method makes things messy. While it is important to keep the length of a transaction as short as possible, it CANNOT be done at the expense of the integrity of a the unit of work!

In the example that I scribbled below (and its not a great example), a single unit of work (adding a person with an address and an employer) allows for partial completion. This isn’t what was desired in the first place.

[Stack OverFlow: DAO PAttern-Where to Transactions Fit In?]
[Java Bouique: Managing DAO Transactions in Java]

Hibernate: All Entities Must Have an Id–And All Tables Should Be Entities!

Sometimes the relational database designer in me sometimes wants to create a table with multiple columns without a primary key (i.e., only a foreign key lookup . An example of when this might be appropriate is when some parent data type had many attributes that don’t necessarily need to be persisted as unique entities (any entity being any persisted POJO) on their own. Think of a “customer” table in a database. A customer may have many phone numbers, so a table relationship like this may seem to make sense:

customer
 id
 name
 address

cust_phone
 phone_number
 number_type
 customer_id

In the above example the cust_phone field has no primary key. It has a customer_id field, but this is a foreign key to the customer table. This seems to be a perfectly fine relational database design, but when it comes to Java persistence annotations we run into a few problems. We could go with a join table, but that isn’t really necessary here (we don’t have a many-to-many relationship).

Most importantly, if we want to do an insert or update on cust_phone, we’re going to need to have an annotated class to do it on, and we’re going to have to access that class with an Id (perhaps a composite Id in the case of a join table, but, again, there is little overhead in having a unique artificial primary key on a join or lookup table, and it makes the life of the Java developer easier). When a join table is implemented on the Java side as a POJO with its own Id, we can handle bidirectional updates using those POJOs. If we don’t have this ability we are stuck with navigating a collection of some sort and updating it all at once (at persistence time).

The above can be done using native SQL, but why bother (it would be complicated anyway)? We could also use the parent’s primary key as the child’s primary key, but that isn’t good database design. Ultimately, what makes the most sense is to accept that all Java persisted entities must have a unique @Id. There is little overhead and this prevents a number of problems at insert and update time.

To put it another way, we can think of something like this:

A student has many courses. A course has many students. Between these two entities we may have a student_course table like so:

STUDENT ID | COURSE ID
505        |        11
505        |        12
505        |        13
506        |        12
507        |        15

From a pure relational database point of view, this is fine. Heck, its common!

But to make life easier in the annotated class with this bidirectional relationship, let’s add am artificial primary key to this join table, like this:

ID | STUDENT ID | COURSE ID 
 1 |        505 |        11
 2 |        505 |        12
 3 |        505 |        13
 4 |        506 |        12
 5 |        507 |        15

Now instead of traversing a Hashmap or some other collection of student to course relationships inside of my student POJO, I can navigate each relationship as its own persisted entity class. I can even change an individual student/course enrollment. Although from a pure database point of view, this may be bad practice–we probably instead want some other column, such as “ACTIVE” or “DROPPED” or “REMOVED”. Herein lies yet an additional benefit: We have more details about the relationship. Not only do we know that the relationship exists, but we know some specifics about it, and we can access these specifics through a nice Java class with all sorts of helpful methods. The table above doesn’t have the appearance of a traditional join/lookup table in a relational database, sure, but we’re thinking in terms of database design as well as Java persistence annotations and Hibernate needs.

Here’s another major benefit: As long as my getters and setters account for the bidirectional nature of the object insertions (and they should!), I can add a student to a class or add a class to a list of student enrollments very easily in my Java code:

Class myClass = <some class>;
Student myStudent = <some student>;
myStudent.addClass(myStudent);

Or I can do it the other way:

Class myClass = <some class>;
Student myStudent = <some student>;
myyClass.addStudent(myStudent);

Sure, I could have done the exact same thing with a set of classes using @JoinTable, but when it comes time to manage insertion, modification and remove we end up writing more complicated code and potentially updating more on the database (i.e. we have more transactions than necessary) on the database side. Anther benefit is the ability to query against the join table (in this case student_class) and get a list of unique objects (StudentClassEnrollment), the type of which we know to be a specific POJO.

Now I see the obvious issue above: We have introduced the potential for enrolling a single student into a class twice. This is easy enough to take care of with unique constraints on multiple columns (student_id and class_id) as well as on the DAO/Insert side (in any case, we would want to check for such potential unique constraint violations at the front-end and the DAO end).

Of course, it is very important to remember to create appropriate getter and setter methods so that both sides of this bi-directional relationship (@ManyToOne and @OneToMany) have the necessary key fields. Hibernate will not magically handle this step. The big “duh” moment is when you think about how on earth Hibernate would handle an update on a table with no @Id field on the POJO. It couldn’t, because there is every reason to expect duplicate data on the columns outside of the FK (assuming the FK is not used as a unique ID). Adding to the confusion, there is always the chance (even if you think you have designed against it) that the PK->FK relationship becomes broken, and we are left with orphaned data.

What this all boils down to is designing a database with simplicity of JPA and Hibernate concerns in mind.

[JavaRanch: Hibernate Mapping with no primary key in existing DB]
[Java Persistence: Many To One]

[Thoughts On: Hibernate Many-To-Many Revisited] – They guy has some other thoughts about how to achieve many-to-many mappings and handle for PK. He somewhat agrees with me, but offers a hybrid approach.

The V-Model Approach: Just a Fancy Waterfall

I read this article at MEDS Magazine about Strkyer’s software development process. Stryker is a large and very successful company, so I was a bit surprised to learn that they have had success with the V-Model in software development. In my humble opinion, the V-Model is simply a glorified waterfall model approach, and we’ve seen time and again that the waterfall model is not a good method to attempt.

I don’t believe that most medical device software can thrive in an XP or purely light weight Agile environment either. I believe that we need Agile/Scrum methodologies with some rules and more heavily weighted up-front requirements and design. I’d like to see those of us in the software medical device community come up with our own Scrum, and this may be something that a number of people are eager to collaborate on.

We need to recognize that fixed requirements scope is simply not a reality, in medical devices or otherwise. In the book Agile Software Requirements, Dean Leffingwell points out:

This “fixed requirements scope” assumption has indeed been found to be a root cause of problem failure. For example, one key study of 1,027 IT projects in the Unit Kingdom [Thomas 2001] reported this: “Scope management related to attempting waterfall practices was the single larged contributing factor for failure.”

(i.e., There is no such thing as a fixed requirements scope!) Need we further reason to abandon the waterfall model? Leffingwell offers even more. According to an “oft-cited Standish Group’s Chaos report survey [Standish, 1994]:

  • 31% of software projects [using waterfall approach] will be canceled before they are completed.
  • 53% of the projects will cost more than 189% of their estimates.
  • Only 16% of projects were completed on time and on budget.
  • For the largest companies, completed projects delivered only 42% of the original features and functions.

Ouch!

I’ve never observed a true waterfall in practice. I’ve only seen it attempted. Inevitably, there are modifications to the process, as the team and management realizes that there is a need to revisit requirements and design. I don’t care how much time is spend (wasted) on up-front design… It will never be enough, and there WILL be a need to revisit those stages that the waterfall model insists should have been locked down.

[MEDS Magazine: Developing Save and Effective Medical Device Software]
[Amazon: Agile Software Requirements: Lean Requirements Practices for Teams, Programs, and the Enterprise. Dean Leffingwell]

Brain Rules: Why a Daily Standup Should Be in the Afternoon

I’m reading a book right now called Brain Rules. In it, the author discusses how after a number of hours sitting in front of a computer our brains literally start to call it quits for the day. This happens long before our typical 8 hour workday is up. If your job involves something that requires intense focus, such as writing software, you’re already well aware of this.

I wasn’t thinking of this when I planned my team’s daily standup meetings at 2 in the afternoon. Perhaps a bit selfishly, I was just thinking of when I like to have a break in my work. It turns out that I was probably motivated by the fact that this is a time of day when I feel the need for a change of pace.

I’ve found that when I am head-down into programming, the worst thing to deal with is an interruption. I’ve also found that I can only be hyper-focused on programming for 4-5 hours at a time (at best). After a while I just start to get tired, and its time for a break. The book Brain Rules offers some interesting insight into why this is.

Back to the stand up meeting: Long ago, when I first heard of daily “stand ups” I was alarmed. The last thing I needed was yet another meeting to interrupt an already busy day. What I didn’t understand was the fact that a daily stand up meeting achieves a few important things:

1. It actually reduces interruptions. People who may interrupt otherwise are encouraged to put the interruption off until the meeting.

2. It encourages work. I, and others, always feel like we want to have something to say at the stand up meeting.

3. It discourages long meetings. If the owner of the meeting (the team lead) is wise, he or she will insist that the meeting last no longer than 20 minutes.

4. It provides a much needed break in the afternoon, and an opportunity to refocus. Sometimes, if you live somewhere beautiful like North Carolina, its even a good idea to make the stand up meeting a walk outside. A little exercise and fresh air works wonders after sitting focused on a computer screen for hours.

[Amazon: Brain Rules]

Continuous Integration on Software Medical Device Projects, Part 9

Using a CI Environment to Replace the Traditional DHF

Naturally, an important part of continuous integration is having a CI build that can be checked regularly for continued build success. This is probably what is commonly though of as the key benefit, but there is much more to be gained. Any continuous integration environment that is worth using will allow the team to incorporate packaging of key project items with each build. This includes important documents, tests (both manual and automated test outcomes can be packaged), requirements, design specifications and build results (deployment packages, libraries, executables, installers, etc.). The important thing to note here is the fact that, used wisely, the CI environment can provide a snapshot of all project outputs at any given point in time. Hopefully it is becoming clear that this gives us the possibility of automated DHF creation. Not only that, we have a much more detailed DHF throughout the life of a project and not merely at a point in time in which a particular freeze was performed.

Tests

The continuous integration server should include unit tests (and by unit tests, I mean functional level automated tests) that provide a level of self-testing code such that any build that fails to pass these tests at build time is considered a failed build.

Packaging

 Continuous integration output need not (nor should it) package only built objects. We can leverage CI build integration with our version control system to package everything required per our design outputs (21 CFR Part 820.30 supbart C (d)), design review (21 CFR Part 820.30 supbart C (e)), design verification (21 CFR Part 820.30 supbart C (f)), design validation (21 CFR Part 820.30 supbart C (g)), design transfer ((21 CFR Part 820.30 supbart C (h)), design changes (21 CFR Part 820.30 supbart C (i)) and even our design history file (21 CFR Part 820.30 supbart C (j)).

Read it all:

[CI on Software Medical Devices, Part 1]
[CI on Software Medical Devices, Part 2]
[CI on Software Medical Devices, Part 3]
[CI on Software Medical Devices, Part 4]
[CI on Software Medical Devices, Part 5]
[CI on Software Medical Devices, Part 6]
[CI on Software Medical Devices, Part 7]
[CI on Software Medical Devices, Part 8]
[CI on Software Medical Devices, Part 9]

Continuous Integration on Software Medical Device Projects, Part 8

Build Script Creation

Ant should automatically determine which files will be affected by the next build. Programmers should not have to figure this out manually. While we will use an IDE for most development, we must not rely on the build scripts that are generated by the IDE. There are a few reasons for this:

  • IDE generated build scripts are not as flexible as we need them to be (it is difficult to add, remove and modify build targets).
  • IDE generated build scripts often contain properties that are specified to the environment in which they were generated. Along with this, something that builds okay in one work environment may not build when committed and used by the CI build or when pulled into another environment.
  • IDE generated build scripts very likely lack all the build targets necessary.
  • IDE generated build scripts may rely on the IDE being installed on the build machine. We cannot assume that this will be the case.

The Ant buildfile (build.xml) should define correct target dependencies so that programmers do not have to invoke targets in a particular order in order to get a good build.

Triggering Builds

As noted, we will use Jenkins-CI to automatically perform a CI build every hour if there is a change in the repository. The system will be configured to send emails to the development team if there is a problem with the build (i.e., if a changeset breaks the CI build). It is anticipated that the CI build will break from time-to-time, however, a broken build should not be left unattended. A broken CI build indicates a number of possible problems:

  • A changeset didn’t include a necessary library or path.
  • A changeset caused a problem with a previous changeset, and merge of the changes must be address.
  • A unit test failed.
  • The CI build server has a problem.
  • The build script failed to build the new changeset (missing library or required inclusion).

In my experience, the most common cause of a broken CI build is a lack of attention to the build script. Each developer is responsible to making certain that the ant build scripts are up to date with all required changes. We cannot rely on the build scripts that are generated by an IDE. There are certainly more possible causes that could be added to the above list. It is a good idea for each developer to trigger a CI build immediately following any Subversion commit to ensure that the CI build has not been broken. If a CI build continues to be broken without being addressed, the team leader and/or project manager may revert the offending changeset and re-open any related issue.

Continuous Integration on Software Medical Device Projects, Part 7

Build Labelling

A build is labeled with a predetermined version number (e.g., “2.0”) and with a Subversion changeset number. The beauty of this is that we have a build that is associated with a particular changeset and, by association, an entire set of project documents and sources (as long as we put everything in a single version control system). Once again it should be clear how beneficial such a setup is when thinking in terms of a DHF. No longer do we have to assign a particular team member to fumble through documentation, ensuring that the proper documents are copied to some folder. In fact, we have very little overhead; Our CI server did all the heavy lifting of us!

Mixed Revisions are BAD!

The changeset number, placed in a property file at build time (by the ant build task). If the changeset number has the letter M at the end (e.g., 3001M), the currently checked out fileset is a “mixed” revision.

The current working copy changeset number is easily viewable in TortoiseSVN or at the command line with the svnversion command. It is expected that during development and testing a mixed revision will be used at almost all times. However, any final build must not have the letter M in the build number.

If the letter M does appear in the build number of a formal build, it indicates that there are items in that build that are not up-to-date in the repository, and therefore the build cannot be duplicated using only a changeset or tag. To avoid this, the continuous integration server should be used to create formal builds, and it should be configured to use only a current changeset and no locally modified files.

Ant Build Targets

Build.xml is the Ant build file. It is located at the root level of the project source tree. The following build targets are necessary:

  • build – Compile all code, creating .class files
  • dist – Call the build target and package all code and necessary configuration files, creating a .war file (web application archive).
  • test – Execute automated tests
  • clean – Clean an existing build and dist (remove previously build items)
  • javadoc – Generate Javadoc

Continuous Integration on Software Medical Device Projects, Part 6

Build Scheduling
Jenkins-CI allows teams to set up a project so that a build is performed whenever a change is committed through a version control system. The “Poll Version Control System” option is selected to do this. From there, the team must set up a schedule so that Jenkins will know how often to poll the version control system. Jenkins can be scheduled to poll monthly, daily, hourly, or even every minute. However, I would recommend building hourly (if and only if there has been a change committed).
In the project view, there are lists of the build history. I’ve set up build scripts and artifact archiving so that you can get the build at any point. The Java archive files will often be set up to be self-contained. This just means that all the “stuff” that is needed to run the application (classes, libs, properties, etc) is archived in that single file, and you can always grab it from the CI environment.

To get a current jar (Java executable) go to the build history. A build with a red circle next to it failed. A build with a blue circle is a good build. Click on the most recent successful build and you will see the build #, date and build artifacts. You can download the jar files from here,
This is how we will handle projects in general when someone wants to get a build. There will be more details on tags and so on going forward. We can modify the CI environment to include any artifacts that we want, just as Javadoc or documentation (as long as it is something that we can pull out of Subversion).

Builds are created locally by developers for a number of reasons, however, such builds must always be considered informal. Any build released for formal testing, at the end of a Sprint or iteration or upon project completion must be done in the controlled build environment. Using self-documenting features of code (such as Javadoc), it can be wise to incorporate the output of this extra documentation into the DHF. Why not? There was little overheard in doing so and the benefits are substantial.

Continuous Integration on Software Medical Device Projects, Part 5

What I am proposing in this article is something that I, personally, have never done. In my positions as a software lead, architect, developer and software quality analyst, I have worked only with a DHF as a particular folder with specific subsets of documentation within. This approach has always resulted in a documentation nightmare. I’ve used many version control, issue tracking and continuous integration tools, but I have yet to take the leap to reliance on CI as the source for DHF creation. So while this proposal makes sense, it takes a bit of a leap from the traditional DHF mindset to attempt.

The screenshot (click to see full view) here shows what a (simple) project setup may provide in the way of such packaging. It is up to the team to determine how much or how little the CI handles, but it makes the most sense to allow it to do what computers do very well and what humans tend not to do as well: align things.

The CI Environment
The continuous integration build server should closely mimic the environment in which the final product will be deployed. By doing this, a level of confidence is achieved with regard to system compatibility prior to user acceptance and integration testing. It must also have access (through the version control and/or ECM system) to access all the design controls and documents necessary to build a complete DHF. To this end, I propose using a single version control system for everything. It doesn’t make sense, for example, to store source code in one version control system and documents in another. To do so makes importing of all necessary items difficult, if not impossible. There are a number of benefits to utilization of a continuous integration server during project design and development. Do not think of CI as a tool only for software builds. Integrated with the project version control system, it can serve as much more.

  • Changesets tied to builds
  • “Changeset” is really a Subversion term. For the purposes of this chapter, a “changeset” is what happens any time a user commits a change (be it source code, documentation, graphics, etc.) to the version control system. The CI build should be configured to execute (i.e., build and package) the project when it detects that there is a new changeset.
  • Frequent Builds, Status Update and Rapid Feedback
  • The CI build gives the development team prompt feedback on the build status. If compilation failed, tests fail or some requirement element cannot be packaged, the entire team is flagged immediately. To this end, the entire team will know that a particular check-in has broken something. This feedback will eliminate the fear that an unknown break could be so extensive that progress will come to a screeching halt. The near real-time feedback of the CI build saves valuable time (and stress!) throughout development (and even design).
  • Project Progress Tracking (tickets, tests, etc.)
  • Improved communication
  • Feedback (peer review) is triggered by every changeset, each of which is easily viewable
  • Less overhead for communication
  • Improved team understanding of others’ work
  • Jenkins-CI is used for continuous integration builds. The CI build server runs Ant build scripts and and reports results. It is expected that software developers follow the CI build server to make sure that any code commits do not break the CI build.
  • While the use of an IDE is expected, a development team must not rely on usage of the IDE-generated build scripts for any project.
  • If using Apache Ant for a project build, the development team should create (minimally) these build targets: clean, build, dist, test
  • Ant build scripts are no different than other project code in that they must be written clearly, follow standards and commented. Developers are expected to maintain build scripts. Any code commit that requires a change to the build must include those changes upon commit. This includes addition of a class, library, package, path, etc.

Continuous Integration on Software Medical Device Projects, Part 4

21 CFR Part 820 – DHF Requirements

820.30(e) Design History File (DHF) means a compilation of records which describes the design history of a finished device.
–Device Advice: Regulation and Guidance, Software Validation Guidelines, http://www.fda.gov/MedicalDevices/DeviceRegulationandGuidance

Medical device software is audited and controlled by standards defined by FDA, specifically 21 CFR parts 11 and 820. Many of the requirements laid out in this somewhat difficult-to-understand guidance can be made very easy, second nature even, when we use a continuous integration environment throughout the course of project design and development. Looking specifically at the quality system requirements laid out by 21 CFR Part 820.30, Subpart C – Design Controls, it becomes apparent that a good continuous integration environment can help us to address each. A major consideration, perhaps the major consideration, is the completeness of the Design History File.

820.30(j) Design History File. Each manufacturer shall establish and maintain a DHF for each type of device. The DHF shall contain or reference the records necessary to demonstrate that the design was developed in accordance with the approved design plan and the requirements of this part.
–CFR – Code of Federal Regulations Title 21. Subpart C – Design Controls, Section 820.30 Design Controls

The “or reference” part of this statement stands out. Traditionally, medical device manufacturers have though of the DHF as a physical, self-contained item. But with a project of any complexity, it isn’t difficult to imagine how quickly a DHF may grow into an unruly mess of difficult-to-wade-through “stuff.” Why not simply leverage software tools to make the process seamless? Using a continuous integration build environment, development teams can pull together a baseline of all the elements of a DHF as frequently as they wish to; Furthermore, they can do so with a degree of accuracy that cannot be achieved through the diligent (yet distractible) legwork of a pre-occupied team.

I propose that the DHF need not be a single physical or soft folder with duplicate copies of items. Leveraging the CI environment along with the version control system, it is a much better idea to think of the DHF as a snapshot of all relevant design outputs at a given point in time. To that end, the development team can have many snapshots of the DHF throughout the project lifecycle. To achieve this, they need simply define this process in their standard operating procedures and work instructions.

Continuous Integration on Software Medical Device Projects, Part 3

Jenkins CI

For the purposes of this article, the focus will be on one specific continuous integration build tool, Jenkins CI. This is one of the more popular (open source) tools available. Jenkins CI (the continuation of a product formerly called Hudson) allows continuous integration builds in the following ways:

1.    It integrates with popular build tools (ant, maven, make) so that it can run the appropriate build scripts to compile, test and package within an environment that closely matches what will be the production environment.
2.    It integrates with version control tools, including Subversion, so that different projects can be set up depending on projection location within the trunk.
3.    It can be configured to trigger builds automatically by time and/or changeset. (i.e., if a new changeset is detected in the Subversion repository for the project, a new build is triggered.)
4.    It reports on build status. If the build is broken, it can be configured to alert individuals by email.

The above screenshot gives an example of what a main page for Jenkins CI (or any CI tool) may look like. It can be configured to allow logins at various levels and on a per-project basis. This main page lists all the projects that are currently active, along with a status (a few details about the build) and some configuration links on the side. These links may not be available to a general user.

Clicking any project (“job”) links to further details on the build history and status. This image provides us details on what the overview screen in the CI environment might look like, but it is at the detailed project level that we see the real benefit of packaging that can be performed by a well setup CI environment.

Continuous Integration on Software Medical Device Projects, Part 2

Continuous Integration refers to both the continuous compiling and building of a project tree and the continuous testing, releasing and quality control. This means that throughout the project, at every stage, the development team will have a build available with at least partial documentation and testing included. The CI Build is used to perform certain tasks at certain times. In general, this simply means that builds are performed in an environment that closely matches the actual production environment of the system. In addition, a CI environment should be used to provide statistical feedback on build performance, tests, and incorporation of a version control system and ticketing systems.  In a development environment, the team may use a version control tool (i. e. Subversion) to link to tickets. In this way, any CI build will be linked to a specific changeset, thereby providing linkage to Issues, Requirements and, ultimately, the Trace Matrix.

A development team should attempt to perform continuous integration builds frequently enough that no window of additional version control update occurs between commit and build, and such that no errors can arise without developers noticing them and correcting them immediately. This means that for a project that is in-development, it should be configured that a checking triggers a build in a timely manner. Likewise, it is generally a good practice for the developer committing a changeset of code to verify that his or her own changeset does not break the continuous integration build.

Allow me to address the word “build.” Most software engineers think of a build as the output of compiling and linking. I suggest moving away from this narrow definition and expanding it. A “build” is a completion (in both the compiler sense and beyond) of all things necessary for a successful product delivery. A CI tool runs whatever scripts the development team tells it to run. As such, the team has the freedom to use the CI tool as a build manager (sorry build managers, I don’t mean to threaten your job). It can compile code, create an installer, bundle any and all documents, create release notes, run tests and alert team members about its progress.

Continuous Integration on Software Medical Device Projects, Part 1

I’m currently working on an article about continuous integration on software medical device projects, and how the CI environment can actually be used to solve many of the design and tracing requirements that must be dealt with on such a project. I’m not finished, but I wanted to post a little bit here. Here goes.

A continuous integration (CI) tool is no longer simply something that is “nice to have,” during project development. As someone who has spent more time than I care to discuss wading through documents and making sure references, traceability, document version and design outputs are properly documented in a Design History File (DHF), I hope to make the value of using CI to automate such tedious and error prone manual labor clear. CI shouldn’t be though of as a “nice-to-have.” On the contrary: It is an absolute necessity!

What is Continuous Integration?
In software engineering, continuous integration (CI) implements continuous processes of applying quality control — small pieces of effort, applied frequently. Continuous integration aims to improve the quality of software, and to reduce the time taken to deliver it, by replacing the traditional practice of applying quality control after completing all development.
- Wikipedia: Continuous Integration

When I say that continuous integration is an absolute necessity, I mean that both the CI tool and the process are needed. A CI tool takes the attempts—sometimes feeble attempts—of humans to make large amounts of documentation consistently traceable and forces the computer system to do what it does best. The use of a CI tool is not simply an esoteric practice for those who are fond of its incorporation. As you will learn in this chapter, continuous integration is something that good development teams have always attempted, but have too often failed to utilize software tools to ease the process. Going a step further, development teams can use a CI tool to simplify steps that they may never have dreamed of before!

What is a Software Medical Device?

When writing software for medical purposes, that software may or may not be subject to FDA scrutiny. We may or may not be required to submit for a 510k. What does this mean? How do we know? Its all a little confusing.

As I considered a series of articles on the subject, I wanted to navigate through 21 CFR 820.30 — Quality System Regulation, and explain implementation of a quality system for each item in subpart C–Design Controls. The first item, however, deals with medical device classification. This is something that should be left to regulatory and FDA and not an design team working on the quality system. We are not fully qualified to determine whether or not we are working on a medical device, or what classification it is. Left to our own, we can likely come up with many great excuses as to why we think our product is not a medical device!

In subpart C—Design Controls of 21 CFR part 820, we are presented with the following:

(a) General.(1) Each manufacturer of any class III or class II device, and the class I devices listed in paragraph (a) (2) of this section, shall establish and maintain procedures to control the design of the device in order to ensure that specified design requirements are met.(2) The following class I devices are subject to design controls:
(i) Devices automated with computer software; and
(ii) The devices listed in the following chart.

Section Device
868.6810                     Catheter, Tracheobronchial Suction.
878.4460                     Glove, Surgeon’s.
880.6760                     Restraint, Protective.
892.5650                     System, Applicator, Radionuclide, Manual.
892.5740                     Source, Radionuclide Teletherapy.

Thomas H. Farris, in Safe and Sound Software – Creating an Efficient and Effective Quality System for Software Medical Device Organizations, offers us a definition of a medical device:

 

Medical Device

Any equipment, instrument, apparatus, or other tool that is used to perform or assist with prevention, diagnosis, or treatment of a disease or injury. As an industrial term of art, a “medical device” typically relates to a product that the FDA or other regulatory authority identifies as a regulated device for medical use. [2]

I’ve been contemplating a detailed writeup on this subject, but I haven’t had a good idea of where to begin, especially since my own regulatory experience is, at best, limited. Today I stumbled upon this article on the subject over at MEDS Magazine. Bruce Swope (the author), offers a little bit of insight on the 3 medical device classifications:

Generally, these three classes are determined by the patient risk associated with your device. Typically, low-risk products like tongue depressors are defined as Class I devices, and high-risk items like implantable defibrillators are defined as Class III devices. The marketing approval process is usually determined based on a combination of the class of the device and whether the product is substantially equivalent to an existing FDA-approved product. If the device is a Class I or a subset of Class II and is equivalent to a device marketed before May 28, 1976, then it may be classified as an Exempt Device. A 510(k) is a pre-marketing submission made to the FDA that demonstrates that the device is as safe and effective (substantially equivalent) to a legally marketed device that is not subject to Pre-market Approval (PMA). For the purpose of 510(k) decision-making, the term “pre-amendment device” refers to products legally marketed in the U.S. before May 28, 1976 and which have not been:

  • significantly changed or modified since then; and
  • for which a regulation requiring a PMA application has not been published by the FDA.

PMA requirements apply to Class III pre-amendment devices, “transitional devices” and “post-amendment” devices. PMA is the most stringent type of product marketing application required by the FDA. The device maker must receive FDA approval of its PMA application prior to marketing the device. PMA approval is based on the FDA’s determination that the application contains sufficient valid scientific evidence to ensure that the device is safe and effective for its intended use(s). For some 510(k) submissions and most PMA applications, clinical performance data is required to obtain clearance to market the device. In these cases, trials must be conducted in accordance with the FDA’s Investigational Device Exemption (IDE) regulation.

Ultimately, however, we cannot classify our own software medical device. That is the job of the FDA.

[MEDS Magazine: Executive Overview of FDA Medical Device Approval Requirements]

[1] 21 CFR Part 820—Quality System Regulation

[2] Safe and Sound Software – Creating an Efficient and Effective Quality System for Software Medical Device Organizations, Thomas H. Farris. ASQ Quality Press, Milwaukee, Wisconsin, 2006, pg. 118