As Simple as Possible, But Not Simpler
Tags: apm, simplicity, demos
Published: 2023-06-06 04:00:00

n building the demos for my "absolutely simple" series, I've worked hard to stick to the spirit of one of Albert Einstein's famous quotes:

> "If you can't explain it simply, you don't understand it well enough." - Albert Einstein

The idea was that I would strip my examples down to the barest essentials - not a single line of code more than what was needed. No additional elements, files, containers or whatnot. 

Of course, the intent isn't to stop at that (admittedly simplistic) level, but to use it as a foundation to establish a basic understanding of the tool, technique, or feature; and then build to more real-world scenarios from there.

As I worked on the next entry in my "absolutely simple" series, I tried to build a demo for application performance tracing (or APM). In most of my previous efforts, I always had a sense that - given enough time - I could probably have made my example simpler still. More stripped-down. More "atomic", if you will.

However, in my quest to create the simplest possible example of APM, I ran into the essence of a different Einstein quote:

> "Everything Should Be Made as Simple as Possible, But Not Simpler" - Albert Einstein (paraphrased)

I build a demo of an app that was just 10 lines, but refused to show up New Relic's APM screen.

```
import newrelic.agent
newrelic.agent.initialize('newrelic.ini')
from passeo import passeo

@newrelic.agent.background_task(name="NameyMcNameyFace", group='GroupyMcGroupFace')
def execute_task():
    print(passeo().generate(length=10, numbers=True, symbols=True, uppercase=True, lowercase=False, space=True, save=True))

execute_task()
newrelic.agent.shutdown_agent(timeout=10)
```

For context, this script leverages the open source [Passeo](https://github.com/ArjunSharda/Passeo) library, which generates passwords and/or checks existing passwords to ensure the pass a basic strength check. The problem (from an APM perspective) is that my script was designed as a "run and done" type utility rather than a persistent application. Perfect for the busy sysadmin (my kind of people), but very bad if you want to trace the performance of an application over time. Because effectively, there *IS* no time.

Once a friend helped frame the root cause, I found a way to bulk it up a bit:

```
import newrelic.agent
newrelic.agent.initialize('newrelic.ini') #This is required!
from passeo import passeo
import requests
import json
import time

inputurl = "https://random-word-api.herokuapp.com/word"

@newrelic.agent.background_task(name="NameyMcNameyFace", group='GroupyMcGroupFace')
def execute_task():
    getword = requests.get(inputurl).json() # Call the api to retrieve the statistics
    print("Password is "+getword[0])
    print(passeo().strengthcheck(getword[0]))

counter = 0
while True:
    execute_task()
    time.sleep(5)

newrelic.agent.shutdown_agent(timeout=100)
```

The modified code does three things missing from the original version:
 - First, by adding some pauses and delays, each operation takes a little longer, giving the APM agent time to sink it's teeth into everything going on.
 - Having the code make a call to an external API (to grab a random word out of the dictionary) also adds to the overall execution time. 
 - And finally, I had to add a special delay to the New Relic APM agent, so it didn't clean itself up too quickly and miss some of the trace data.

With those changes in place, my application tracing now shows up.
<image: show new code in NR APM screen>

This isn't the actual "absolutly simple APM" blog, so my goal isn't to describe how APM works, or the other steps I took to get this instrumented.

Instead, I wanted to talk this experience, when I discovered the limits of simplicity, and had to embrace just a bit more complexity to get where I needed to go.