Debuggers aren’t much use when you first begin working on a bug, because before you can use one you need to know where to set your breakpoints.
When debugging with AppMap, you don’t need to know where the bug is. You can start from a high-level function, such as an HTTP server request, or you can go “bottom-up” and start with a low-level function or SQL query. Either way, an AppMap provides interactive maps of all the factors that might be contributing to the bug, and helps you figure out where things are going wrong. You can quickly navigate around the source code, knowing which code is relevant to the bug, and which code isn’t. And you can see parameter values, HTTP server and client requests, and complete SQL queries. In many cases, you won’t even need the debugger; just AppMap and your rubber ducky.
A breakpoint will tell me exactly what’s going on in the code. But how am I supposed to know where to put it?
The AppMap Trace view provides a detailed, interactive picture of code behavior. So, the first step in debugging with AppMap is to record a test case or example scenario that reproduces the bug. Once you have the AppMap you need, you can use the Trace view to examine a wealth of information that will usually indicate where the bug is happening in the code base. Parameters, return values, status codes, SQL, exceptions, URLs, route names, function labels, and library functions are all available. Sometimes, a bottom-up approach is more productive than top-down. For example, you may know that the bug is related to SQL, but you don’t know where the SQL is coming from. In the AppMap dependency view, you can click on the Database to see all the SQL queries. You can click on a query and then reveal that query in the Trace view, which will show you the complete code execution context around that query. Even if the query was generated by complicated code or an object-relational mapping library, you’ll see the raw SQL exactly as it was sent to the database.
As you explore code behavior using these methods, you may be able to solve the bug using the AppMap alone. If not, you’ll have a great idea where to set your breakpoints to continue your investigation.
The best way to get started is with a test case which reproduces the bug. If a test case is not available, you have a couple of alternatives:
A failing test case is the best way to start fixing a bug
The AppMap extension for your code editor brings full-featured interactive diagrams for viewing, searching and exploring AppMaps right into the development environment.
The AppMap extension provides a smooth pre-debugging user experience
Like a debugger, an AppMap contains parameter names and values. Unlike a debugger, you can explore the code execution in any order you like. You can jump forwards, backwards, up, down. All the data is pre-recorded, so you can view any event you like, in any order.
The visual AppMap is helpful for debugging in two ways. First, when you look at the Component diagram, you’re only seeing the classes and packages that are actually involved in the buggy code path. So, you can step through the control flow starting from the highest level and drilling down into the details, and you can jump into source code at any point. The Component diagram is like an automatic table of contents of all the code that matters for the bug.
Code flow, variable values, SQL, and source code, all integrated together
Second, when you spot something in the code flow that looks useful or suspicious, you can jump over to the Events view. Here you can see all the details of the functional call tree, and you can see details such as:
If there’s a problem with this flow, you definitely need to get to the bottom of it…
It’s often possible to spot the bug from this information alone. But if you can’t quite figure out where the bug is happening, the source code which is linked from the diagrams provides ideal places to set breakpoints.
Now you’ve got a test case that reproduces the bug, and you’ve got a good idea of where to set breakpoints in order to pinpoint the problem. Run the test case again, and see if your hypothesis checks out.
This video shows how to use AppMaps for documentation of code issues and fixes.
Identify the N+1 anti-pattern in Java.
Identify the N+1 anti-pattern in Rails.
At AppLand, we make a free and open source tool called AppMap for VSCode that helps developers deliver high quality code by providing better insights into code design and behavior. But even though code design is our company mission, we still make the same mistakes as everyone else!
In a series of posts, I am going to share five code design mistakes that we’ve made in the last three months. In each post, I’ll provide enough detail that you’ll know exactly what happened. I’ll also discuss how we discovered the problem, how we fixed it, and how we responded by improving our development and code review processes. My aim is to provide you with information and suggestions that you can use to avoid making some of these same errors!
Before we get into the first example, let’s take a look at the nature of code design mistakes. First, we find it helpful to divide these issues into four categories: performance, stability (bugs), security, and maintainability.
Over the last decade or so, static analysis tools have been able to automatically detect some problems in these four areas. However, the MITRE list of Top 25 Most Dangerous Software Weaknesses contains the following statement about the four weaknesses that have moved farthest up the list:
All four of these weaknesses represent some of the most difficult areas to analyze a system on.
Digging into the list a little bit, what they mean by “difficult to analyze” is “not findable by static analysis”. That means that developers can’t rely on static analyzers to maintain code quality. The best tool we have to find and fix complex problems is code review, and that’s why better code review practices feature prominently in the recommendations that we give for each of our examples.
With that in mind, let’s get to our first mistake.
This mistake made it into production, and we had to roll back and fix it
Slow queries can be a real problem. Not only can user experience be degraded, but if the site performance is bad enough, web servers and gateways will time out (typically after about 30 seconds) and the app becomes completely unavailable. Our AppLand Server product contains about 1 million AppMaps totaling about 1 TB of data. This isn’t FAANG scale, but it’s still big enough that inefficient queries can cause problems. It happened to us…
One fine day, after an update, we discovered that the homepage of our app was frequently timing out. Using MiniProfiler, we discovered that a slow query was causing the problem. Then, we used the PostgreSQL EXPLAIN feature to analyze the query.
Every team needs someone who can read query plans and optimize queries, and we do have such a person. Once we had the query plan, our database expert was able to determine that the root of the problem was an unindexed join. To say that in English, our query correlates data from two tables together. In the beginning, when our database was small, the query was fast and nobody noticed that it was inefficient (inefficient operations are fast anyway, on small data sets!). As the tables grew larger and larger, the query grew slower and slower, since we weren’t providing the database with an index that it could use to efficiently match up the rows. Adding the index was basically an instant fix. This is Databases 101, frankly. But still, when building a new app, it’s pretty easy for something like this to slip through the cracks.
Maybe you aren’t a PostgreSQL query plan expert. I’m not either. But to avoid introducing a bad performance bug, you really only need to know two things about query plans:
After this experience, we are more careful about allowing complex queries into our code base without a detailed review. Fortunately for us, we have AppMaps of all of our code, and AppMaps show all SQL queries in detail. Therefore, we know exactly what SQL is being introduced in each PR, and we can inspect the query plan before merging each new or modified query. To make it even easier to identify and review SQL changes, we are adding the ability to “diff” the behavior of AppMaps (yes, it will be open source). This new feature reports and highlights changes to important code paths, including SQL.
Here’s an example of an AppMap Trace Diff which shows a
SQL DELETE statement being removed in a proposed code change (with my emphasis added):
In our code reviews, we highlight any changes to queries that might be expensive. Then, if possible, we check the query plan on the production database before deployment, to catch any surprises before we make them live. We’ve also added better monitoring, both on our app, and on the database. Again, this is pretty basic stuff, but startup teams are busy. Sometimes (a lot of the time…) it’s more important to be refining the product to fit the users’ needs than to be worrying about preventing a maybe-going-to-happen-someday problem. This slow query problem reminded us that it was time to “up our game”, in both monitoring and the care we take with SQL.
Thanks for reading! Follow me to stay updated on the next 4 posts in this series.
AppMap for VSCode provides interactive maps and architecture analysis to help you write better Python, Ruby, and Java code. It takes only 2 ¹/₂ minutes to install, configure, and generate diagrams. Check it out on the VS Code Marketplace!
For a full tour of AppMap and code architecture diff in action, check out our demo at the New York Enterprise Tech Meetup.
Originally posted on Dev.to
Rails + Your Code = ❤️. Most of the time!
The one criticism that sticks to the Rails framework (and any other big framework, to be honest), is that Rails does so much for you it can be hard to understand what’s really going on. Usually, we can just follow the Rails cookbooks and watch the magic happen. But sometimes, we really need to understand how Rails (and projects in the Rails ecosystem like Rack, Devise, Sequel, Delayed Job, Resque, Sidekiq, database drivers, etc) actually work. And even more importantly, how they work with your code.
You’re probably already familiar with REPL and debugging tools like pry and byebug. These tools take an “inside-out” approach to troubleshooting. You choose a point in your code where you want to start, and then you can explore outwards from there. But while you can get a lot of detailed information that way, it’s hard to build an understanding of what’s going on overall in the codebase.
So what happens when you don’t even know where to start? Then you need more of an “outside-in” approach. Here’s a cookbook you can follow to see how your code is working with Rails and other gems, starting from the widest scope and narrowing in on details.
We will be using a an open source tool called AppMap for VSCode (disclaimer: I’m an AppMap developer). AppMap automatically builds diagrams of software architecture and runtime behavior. Here’s how you do it.
appmap gem by following the instructions in the README. Here’s a quick checklist:
appmapgem to the Gemfile.
test_helper, if applicable.
appmap.ymland configure the
Quit your VSCode session and use this Gist to open a VSCode project with all the Rails source code. You are doing this so that you will be able to click into dependency gem source code from the diagrams.
When you run the
rails_gem_paths.rb script, it will print out a list of
active* gem names.
Copy and paste these lines into your appmap.yml file, so that it looks like this:
name: my_project packages: - path: app/models - path: app/controllers ... any other project source folder - gem: rack - gem: paperclip - gem: actionview - gem: activerecord ... any other dependency gem you want
You can also add other gems, such as
imagemagick, etc. Whatever you need.
Here’s an example appmap.yml that I use with the my fork of the Rails Sample App 6th Edition.
Now you have a choice of how to record your code.
Option 1: Run tests
If you have an RSpec or Minitest test that performs the behavior that you want to observe, run this test with
Option 2: Run the server and use the app
If you don’t have a test that does what you need, run the server with
Then open your app in the browser and interact with it. If your app is an API server, run the server and send API requests. Either way, you’ll get an appmap.json file when the server exits.
AppMap for VSCode enables you to open any *.appmap.json and explore it visually. If you ran test cases, you can find the diagrams in tmp/appmap/[minitest | rspec]. If you generated an appmap.json, rename it something representative, like login.appmap.json. Then click on it in VSCode to view it.
Here are some diagram examples, for which I’m using the Rails Sample App 6th Edition. I mapped a test case in which a user signs up for the sample app using valid information.
When I open the AppMap, the first thing that I see is the Dependency map, which shows the HTTP server requests, code packages (both app code and the gems we included in appmap.yml), as well as the database. Lines on the graph correspond to function calls (not just statically inferred dependencies; actual runtime calls). You can see how the controller makes calls to
activerecord. On the left hand side, you can see the controller methods which are used in this test case (
show), as well as the inbound and outbound functions.
You can navigate from any code component to the source code, and you can step from the dependency map view over to the detailed Trace view. The Trace view shows details of code execution, including parameter values, return values, and SQL.