I’ve been a developer on the Rustici Engine team at Rustici Software for a little over a year now, and in that time I’ve learned all about various learning standards and how our products support them. Recently, I was tasked with implementing support for a new standard called Learning Tools Interoperability (LTI) in Rustici Engine. I knew very little about LTI before this, so I began my journey into learning the standard the way most developers would: by reading the specs. It wasn’t long before I realized there was a lot of information to take in—the difference between platforms and tools, the single and multi-tenant integration models, the various versions of the specification, and the security implementation details associated with each version. Now that I’m on the other side of that work, I’m here to share my experience with learning and implementing a standard like LTI from the ground up.
The LTI model
LTI defines a standardized way for third-party content to be integrated into courses within an LMS or platform. Without LTI, learning management systems and content providers would have to rely on custom integrations—meaning new development work would have to occur every time an LMS or content provider wanted to integrate with the other. With LTI, however, you can implement support for the standard once and then your product can easily integrate with as many systems as you want, simply by sharing the required configuration information. If you’re wondering how this model compares to SCORM, check out this blog post.
Deciding where to begin with LTI
If this sounds interesting and your organization is considering adding support for LTI, there’s a few things to consider.
Building LTI for a Tool versus Platform
The LTI spec defines two main concepts: platforms (previously called “tool consumers”) and tools (previously called “tool providers”). In broad terms, a “platform” is the entity from which you launch into a “tool”. If your organization is an LMS, you’ll probably want LTI platform support. If you have content, such as courses or activities, that you’d like to provide to users, you’ll likely want LTI tool support.
Comparing LTI 1.1 versus 1.3 features
With the platform vs. tool distinction in mind, you’ll need to decide which version(s) of LTI to support. LTI 1.1 combines the LTI 1.0 (previously called Basic LTI) spec with a Basic Outcomes Service for reporting scores. LTI 1.3 consists of a core specification that can be extended to be LTI Advantage Complete by implementing the Names and Roles, Deep Linking, and Assignment and Grade Services specifications. Which version you’ll want to support depends largely on which tools or platforms you would like to integrate with (you’ll need to match the version the other system has exactly as they are not backwards-compatible). It’s also important to note that though versions 1.0 and 1.1 are currently considered deprecated, most of the certified LTI products implement these versions, so adding support for these standards will maximize the number of products yours can integrate with.
Getting started with LTI development
Once I had a good sense of LTI and how it is intended to be used, it was time to begin the development process.
Technical differences from LTI 1.1 to 1.3
I chose to implement LTI 1.1 platform support first, and this ended up being a good choice for a few reasons. For one thing, the 1.1 launch process is much simpler than 1.3. Launching an LTI 1.1 link from a platform requires just one request to the tool, and recording grades via the Basic Outcomes service requires the addition of just one new endpoint. As an added bonus, some of our products already had LTI 1.1 tool-side support, so I was able to test my implementation with SCORM Cloud Dispatch as well as Rustici Dispatch. Developing support for LTI 1.3, on the other hand, required more effort: there was additional configuration information that needed to be exchanged, implementing OpenID Connect required several new API endpoints, and there was now a need to generate and manage key pairs—something we had never needed to do in Engine. At this point, the LTI domain knowledge I had from implementing version 1.1 proved to be quite helpful—once I had gone through the process of implementing one version of the standard, it was easier to understand and implement what version 1.3 required. As LTI 1.3 was a newer standard, there also seemed to be less resources online for developers. There were times when I was searching for clarification about a claim in the specification or for an example of how another product had implemented part of the standard, and there were so few results available that it felt like playing a game of GoogleWhack.
Testing my LTI integration
The biggest challenge I’ve had while developing LTI support is finding ways to test the integration between my platform implementations and LTI tools. Whenever you have a piece of software that integrates with an external system (like the LTI standard specifies) the number of places and ways in which things can go wrong increases as you don’t just have to worry about your own product—you also have to worry about the product you’re integrating with and everything going on in between. Additionally, when errors happen it’s not always clear whether the tool or platform is causing the error (you’re often at the mercy of the other system’s logging and error reporting capabilities). I relied on the IMS test tools, which are a good benchmark for making sure your implementation works, but came up short in the search for additional tools and platforms to test with (LTI 1.3 is a newer standard, so it was especially difficult to find options for testing this version).
If you’re deciding whether to purchase support for LTI or implement it in-house, my recommendation depends on whether you are developing a new product or adding support to an existing product. In the case that you are doing greenfield development, it might make more sense to try implementing support yourself as standing up LTI support in an isolated environment should be mostly painless. On the other hand, adding LTI support to an existing product turns out to be quite a gnarly problem. To give you some context, I spent about six months dedicated to this task, and a lot of this development time was spent trying to make the existing Engine architecture work with LTI. In other words, though LTI itself is not inherently complex, if you’re considering adding support for it to a legacy product you should count on it introducing a fair bit of complexity into the development process.
If you have any questions about my journey or are interested in adding LTI support to your platform through our Rustici Engine product, please reach out and ask us anything as we’re here to help.