 I'm really happy to introduce our next two speakers, Lisa Cockrell and Jordan Upperman. Lisa is the Director of Application Development at IssueTrack and Jordan is a lead full-stack developer on their product. Hey, I'm Lisa Cockrell and I've been with IssueTrack since 2004. In those 16 years, I've been a software tester, a new feature developer, I've managed the QA team and I've managed the dev team. Today I'm the Director of Development for IssueTrack and I'm also the Acting Scrum Master for our product team. My name is Jordan. I started at IssueTrack as an automated test engineer back in 2017. A little over a year ago, in early 2019, I was given the chance to work on the product development team as a full-stack developer. It was around this time that IssueTrack was really leaning into our adoption of Scrum and I received my Scrum Master certification followed by my Scrum Developer certification. Since then, I've been trying to find innovative ways for my team to evolve and further automate or streamline our processes. To give you some background on IssueTrack, we're a web-based ticketing solution that allows companies to track issues so that nothing falls through the cracks. We are a privately owned company with over 2,000 customers across nearly 40 countries. We provide cloud and premise deployment options to suit our customers' unique needs and our headquarters is located in Southeastern Virginia. We were founded in 1992 as a custom development company and our first iteration of the IssueTrack product was provided in the year 2000. As you can imagine, a 20-year-old product has a lot of legacy code. So we're going to be sharing the story of our transition to agile practices in the context of our adoption of GitLab. There were a number of reasons we took interest in GitLab, but the primary reason was a feeling of tool bloat. We were using several tools at the time and this presented innumerable challenges, one of which was the cost. Having multiple different tools and maintaining usage subscriptions for each member of the team resulted in costs that were becoming colossal. There's also the complexity of coordination. Creating a multitude of tools working together creates a lot of variables and thus more room for things to go wrong. But even when things go right, there's a productivity cost with context switching between tools that should just be avoided whenever possible. We had some trepidation when it came to switching as migrations of this scale are rarely simple. A lot of planning and consideration goes into adopting a new tool such as GitLab. Considerations can vary from training the team on the new processors, potential downtime during the migration process, and the possibility of data loss. Data loss could result from the actual migration process itself, or just an inability to adequately represent data from an old tool in the context of the new tool. Another major factor is any possibility of complexity or of the migration process itself. For example, if we had to manually copy every item from one tool to the other, that would have just been a non-starter. Thankfully, these were largely non-issues when it came to GitLab. GitLab provided a simple, easy-to-use method within their user interface for migrating our repositories and tickets from GitHub. While a similar option did not exist at the time for JIRA, we were able to use the GitLab API to programmatically import our ticket information from that platform. The GitLab API documentation made this task much more straightforward than we had initially anticipated. The ease of use did not end with GitLab's documentation, however. Our company fell in love with the intuitive nature of the tool and its use now spans beyond the development department and throughout the rest of our DevOps team, including our cloud operations, tech ops, and IT departments. The impact of IssueTrack's adoption of GitLab as a one-stop tool as well as our embracing of the agile software development methodology was huge. Probably all of our daily workflow tasks have benefited. We'd like to tell you about a few of the more impressive changes. While our main objective in adopting GitLab was to make our processes more lean, an added benefit was the ability to make our monthly costs lean as well. Prior to our transition, we were using five separate tools for our daily workflow processes. AppVayer and CircleCI for our CI CD pipelines. GitHub for our repositories and ticketing solutions, along with Waffle.io for a Kanban tool. We had recently onboarded JIRA for a separate ticketing solution as well. The subscription cost per month for all the licenses that we needed was substantial. But once we moved to GitLab, we were able to bring that cost down significantly, a savings of about 80% per month. Another important way that GitLab assisted us in our transition to agile was by allowing us to better manage our product backlog. Prior to GitLab, our backlog resembled a black hole. Things would go in only to never be seen again. There was very little conformity in our tickets, which made it challenging to group or categorize them at scale. It was difficult to prioritize tickets, and as a result, tickets were worked at developers discretion, which in turn made our output as a department unfocused, resulting in major time gaps between releases. At times, work would begin only for the developer to realize that the amount of information on the ticket was insufficient. This stemmed from a lack of a definition of ready. The ticket template featuring GitLab proved to be extremely useful for our backlog management. We now leverage templates to communicate what information is needed when the ticket is submitted. We have a dedicated template for every source of tickets. The product owner team uses the user story template. QA uses a QA bug report template. Customer complaints from our TSAs are added using the support bug report template and on and on. We use the quick action feature of GitLab within our templates to ensure uniformity of labels in our tickets. This prevents us from having to train everyone on which labels are appropriate for which kind of ticket. Instead, the appropriate template is chosen and all the relevant labels are automatically applied. Our utilization of these features has finally given us the ticket conformity that we sorely lacked. Another GitLab feature that we leverage heavily is the ability to customize our Kanban boards. Our team had some experience with Kanban boards prior to our transition to GitLab, but we were limited to a single board with static columns. We didn't realize what we were missing. Any ticket that wasn't currently in process was in the open column, a.k.a. the black hole. This made it difficult to refine and groom our backlog, so much so that we were copying and pasting ticket titles into a Google Sheet in an attempt to give it structure. When we migrated to GitLab, our ability to leverage the Kanban boards was a game changer. We now have multiple boards at both the group and the project levels, and the ability to select custom columns allows us to tailor these boards to our needs. There are two major boards that we use to process our workflows, the PO board and the Sprint board. Because of GitLab's customizable Kanban board feature, we created a new board specifically for our product owner. This board tracks our backlog refinement process and ensures that all of our tickets meet our definition of ready. The PO team now has the ability to focus solely on prioritizing the work that is coming down the pipe. We created this board with three columns, unrefined, groomed, and ready for Sprint. User stories are entered by our PO and placed into the unrefined column. The development team meets with the PO several times a week to discuss and design the new features, eventually creating and weighing discrete test tickets associated with those user stories. At this point, the developers move the user story ticket into the groomed column. The groomed column is not prioritized and is merely a holding container for the PO team to select work for the final column of this board, the ready for Sprint column. By the time a ticket reaches this column, each user story has a story point estimate. This new information gives the PO team the ability to prioritize our work and estimate release schedules with more data than they had when the user story was initially submitted. Our second customized Kanban board is our Sprint board. This board starts with the ready for Sprint column and contains columns for the rest of the development process. Because our two boards share the ready for Sprint column, we can rest assured that every ticket that appears on this board meets our definition of ready. The duration of our Sprint planning meetings has been greatly reduced. Now we can simply pull tickets from the ready for Sprint column until we've reached a weight that is equivalent to our average velocity, all without having to examine the tickets themselves. And the ticket counts and weights are automatically calculated and listed at the top of each column. Our use of these two Kanban boards allows us to pivot with ease when necessary as bugs are found during testing. It's easy for us to quickly weigh the new ticket, remove an item with equal weight and send it back to the top of the ready for Sprint column. This practice prevents scope creep in our Sprints and reminds stakeholders that when work is added to an already committed Sprint, something must come out. Our general ticket workflow has been transformed as a result of our transition to GitLab and to Scrum. As mentioned before, prior to our transition, tickets priorities were unclear and were worked at the discretion of the developer. More often than not, when the developer began on a ticket, there was minimal information and this resulted in some features being developed without the intended context of the initial request and subsequently a loss in value for the customer. Without our refinement and grooming efforts, it was difficult to predict the level of effort of each request and therefore planning and scheduling were cumbersome processes. This caused deadlines to be missed and unhappy stakeholders as well as customers. Now with our system of grooming tickets in place, it's much easier to select a new ticket as they all meet the definition of ready, which includes estimates regarding the level of effort for each ticket. Having our backlog in a consistently prioritized fashion allows the team to select work quickly and without contemplation, further reducing the effort needed to produce deliverables. We've also implemented several systems to shorten our feedback cycles, one of which being improvements to our CI CD process. Before GitLab, we were forced to use two different tools for our CI CD. We used AppBear for our .NET projects and CircleCI for everything else. As these tools were separate from our repositories, the CI CD results were obscured and required us to context switch in order to consume the information that they provided. Another drawback of these tools was the lack of control over the process. We relied on the runners provided, which may or may not have been provisioned sufficiently for what we needed them for. This method had implications with security policies for our cloud storage and setup times. In terms of debugging problems in our pipelines, a major setback was the monolithic nature of the pipeline execution as a single stage. The GitLab CI CD functionality allowed us to create multi-stage pipelines which greatly aided in troubleshooting failing builds. Build failures can be viewed via the GitLab user interface and the multi-stage pipeline allows us to easily pinpoint the step that failed without having to retry the entire pipeline or go digging through log files. Today, it's much easier to block emerge requests if the pipeline fails, which reduces the number of bugs that are entered into our product. The ability to host our own runners has also been a game changer. We have sped up our pipelines by ensuring dependencies are present before starting our builds. Another major benefit is the ability to control the security policies on the runners themselves. Rather than using access keys to interact with our cloud resources, we set AWS Identity Access Management Goals on the runners themselves, which makes it easier to store our build artifacts. But it has also allowed us to start leaning into infrastructure as code as a company. We leverage infrastructure as code through the automated deployment of Lambda Functions with associated API gateways. We've developed internal tool sets that employ infrastructure as code, like our Trackbot, which runs our Slack integrations, but we'll talk about those later. We also use it for our Help Center Resolution service, which drastically reduces the amount of coordination that is needed between our development team and our tech writers. Another way we use infrastructure as code is our self-service site deployment app, which lessens the frequency of requests from our sales team to our cloud team by allowing a site to be automatically deployed to production with the click of a button. Finally, our webforms processing service, which provides the ability to host a custom form for an unauthenticated ticket submission into an issue track application, also runs as a serverless Lambda function whose infrastructure is defined as code within our GitLab repository. Infrastructure as code has also made its way out of the development department. Our internal IT department uses infrastructure as code to maintain user permissions in AWS via IAM roles. While we use infrastructure as code for some of our PC2s in development, such as is the case for automated Selenium testing infrastructure, our cloud operations team uses that much more substantially for automating the process of standing up new servers in our customer cloud, which has been a major milestone in our revitalization as a DevOps team. Adopting GitLab and our switch to Agile have both increased our ability to ensure the quality of our code. In the past, our developers employed the bad practice of merely throwing tickets over the wall, and therefore the QA team's only resource was the developer's documentation on the ticket with no prior knowledge or understanding of the feature. This practice unfortunately allowed for unnecessary refactoring, which had the potential to cause problems that went unnoticed by testers. Without our scrum processes, we were guilty of cowboy coding. And as a result, there was little collaboration when it came to the development of new features. And this created a lot of technical debt by generating an inconsistent code base. We now practice a technique known as paired testing. Developers will pair with a tester before a single line of code is written to discuss the intended changes and potential concerns either person has regarding the plan of action. This allows developers to gain crucial insights from the minds of testers before starting the code. The pair's continued communication throughout the life cycle of the ticket prevents a substantial amount of rework. GitLab helps us achieve this by allowing multiple assignees on a ticket simultaneously, a feature that wasn't offered with the other tools we were using at the time. The feature of linking merge requests to tickets in GitLab allows testers to review the code that was modified and to catch potential unnecessary refactoring problems. Gone are the days when testers were completely in the dark. And today we have far fewer unintended consequences when our product is released to the wild. Stakeholder feedback is another important tool we use to ensure code quality. In the old paradigm, feature acceptance was a very disorganized process. Various stakeholders would make requests often directly to the developers with little cross communication with their cohorts. Of course, these requests were never documented. The results, changing priorities, shifting requirements, rework and delayed output. This exact scenario happened when we were asked to implement a quick search bar in our product. The desk of the developer responsible for this feature quickly became a common destination for business side employees of the company. This resulted in bloated functionality that in production was so non-performant that it ended up being removed altogether. We solved this through our adoption of Scrum which requires a dedicated product owner as well as sprint reviews. PO approval is now required at several stages throughout the development cycle. Using another favorite feature of GitLab, we now mark the user stories blocked until work completes on all the associated discrete task tickets. We have a dedicated column on our sprint board named active user stories where user stories are placed when any of the work tickets have been chosen for the current sprint. Once the story loses its blocked indicator, the new feature is ready to show to our PO team at the next sprint review. We also now insist that all changes and requirements be documented on the tickets themselves ending the verbal communications that would often be lost in time. This has dramatically improved our transparency. Speaking of transparency, GitLab's out of the box integration with Slack allows stakeholders a quick peek into our daily lives. Certain actions within GitLab will cause a post to appear in our DevOps Slack channel. Those actions are when tickets are opened, closed and notes are added. As merge requests are open, approved and merged and build failures will cause a post to that channel as well. As a scrum master, this channel is my gold mine. It is literally a wealth of information about my team's productivity. Finally, I'd like to share ways in which we've made use of the GitLab API in order to minimize the amount of work we have to do manually. The first home brewed Slack integration we've implemented was a tool for automating our branch maintenance. To give this some context, when we release a new update, we push to master and then merge master into all of our protected branches. We've since automated this process using Slack, Express, Lambda, but most importantly, GitLab's API. The first thing that takes place is a developer will enter the command into Slack. If that user is green lit to continue the process, they will be given a confirmation box. If that Slack user is not authorized, they'll simply get a message indicating that they can't proceed. If the user selects to continue, our bot will automatically create a merge request for every protected branch in our project and then systematically check for conflicts using the GitLab API. And finally, prompt us to either finalize our merges when no conflicts are found, or let us know that we need to do it manually and review the changes otherwise. This has helped us streamline our release process and reduce the potential for human error. The last integration I'd like to demonstrate is a tool we created for distributed ticket weighing. Here's a ticket I'll use to demonstrate how it works. The Slack user will enter the ticket URL along with the slash command in order to initiate the process. Using the GitLab API, our bot will automatically parse the URL and determine the project, issue number, and title of the issue. It then tags all the users we specified in our application today. Next, it prompts viewers to vote for a weight they deem appropriate for the issue. A major benefit is that it posts the whole description as a snippet within the Slack thread. This way, the team doesn't even need to open GitLab to participate in the story poker session, which was a complaint we had when using a more generic tool in the past. If you notice up on the top, there's now a new row that's been inserted which contains the names of users we have received away from. These people have voted, but their vote remains hidden to avoid anchoring. We can now weigh tickets asynchronously, which is super useful in these times of distributed work environments. Once either everyone on the waiting for list has weighed in or the reveal button is clicked, we move to the next state. Here we can see the weights selected by the people who have weighed in. We're then presented a dropdown with the weight options and a button to finalize a selection. Users can, at this point, select the appropriate weight after whatever discussion is needed. Once that's done, the dropdown and button are replaced with an indicator showing the final decision. It also updates the original post so the channel we use for the spot stays pretty clean or easy to navigate. Here's where the GitLab API once again comes into play. As you can see, the ticket has now been updated with the weight of two and it's undergone the label changes unique to our process and added a link directly to the thread in Slack where the discussion took place, allowing us to regain the context that led to the decision whenever needed. This has removed multiple manual steps that used to have to take place for every ticket prior to its consideration for work. If you find this tool like it might be useful, it's actually an open source TypeScript Express app we built and you can find the project on GitLab under the name GitLab Slack Story Pokerbot. There's a configuration file within the project that can be used to easily modify the process if that your team needs. Well, we're just about out of time, so I'll wrap up. Agile and GitLab, what were we doing without them? Our product development team is almost unrecognizable now compared to where we were over two years ago. We made the switch and so can you. You belong here. Thank you to GitLab for inviting us here today and thank you for watching.