Roopa Mohan is the principal software engineer for the Code Analyzer team here at Salesforce. She loved computers when she was introduced to them in sixth grade and decided she wanted to do coding. The only company she applied to work at, Salesforce, hired her.

In this episode, Roopa shares information about how Salesforce Code Analyzer came to be and what is in its future. She discusses how it works, who it works best for, and other tools that it works well in conjunction with. 

Listen in to learn more about Salesforce Code Analyzer and how it can be used in your operations.

Show Highlights:

  • What Code Analyzer does for its clients, and who the target audience is.
  • What PMD and its counterpart ESLint are.
  • The customization options for PMD and the code analyzer’s role in the customization.
  • What Code Analyzer 3.0 does for retired JavaScript libraries.
  • What might be the future regarding developer tooling and AI technology.

Links:

Links related to Code Analyzer:

Episode Transcript

Roopa Mohan:
The different fields are modified, added at different parts of time. It’s not like in one place, everything gets added. So we have this object that transforms over time.

Josh Birk:
That is Roopa Mohan, principal software engineer for the Code Analyzer team over here at Salesforce. I’m Josh Birk, your host of the Salesforce Developer podcast. And here on the podcast you’ll hear stories and insights from developers, for developers. Today, we sit down and talk with Roopa about Code Analyzer, what some of its latest features are, and where it’s going, and how it can help you write better code. But we are going to kick things off, as we often do, with her early years.

Roopa Mohan:
Yeah, I think I must have been in… Okay, the first time I even decided computer was my thing was maybe when I was in sixth grade or seventh grade.

Josh Birk:
Oh, wow.

Roopa Mohan:
My uncle had this new computer, it had the Windows 3.1. I was so crazy about it. I used to go to their house all the time, try it out, and then eventually, at some point, I didn’t even have to think much. It’s like, “Yeah, okay. Computer science is what I like and coding is what I want to do.” And yeah, no looking back for a long, long time now.

Josh Birk:
I like it. And how did you get introduced to Salesforce?

Roopa Mohan:
Oh. I was here in Bay Area, I had just gotten married, and this was actually the downtime of the recession and things were slowly starting to look up. So I was in a Visa, a H-4, and then I needed an H-1. And then it so worked out for me that the recession’s turning back was the time I could get my H-1 Visa, and Salesforce was the only company even willing to talk to me. And it was just so, so amazing for me because it was literally the only interview I had and I somehow made it. I’m so grateful for how Salesforce has been treating me, and it’s been a great run. Yeah.

Josh Birk:
Nice, nice. So then now tell me, because last time we talked, you were on the Hammer team, and still love that episode by the way. Really great technical stuff. Tell me about your current role.

Roopa Mohan:
Okay, so I’m right now in a team called Code Analyzer. So to the external world, it’s the Salesforce Code Analyzer, and what we do is help people write clean and secure code. So I’ve always been passionate about how code can be written well, and this kind of gelled right in. So the product we are building is called the Code Analyzer. It’s a SFDX plugin, and then it takes in the power of all these other awesome static analyzers, puts it all in, and then becomes this one single mega tool, which is powered by all the different awesomeness created by different open source communities. And then what it does is it takes in Salesforce code, which can be Apex Visualforce, LWC, and a lot of different things, and then analyzes all of this code and makes sure it’s looking good. So if there are things that are kind of off, or maybe error-prone, or maybe not secure, it throws violations so that developers can use this tool to write good code.

Josh Birk:
Nice. I’m so incredibly dating myself right now, but just your description of several powerful things getting together into one, I’m just getting Voltron vibes off of this right now. So what was the impetus for this? Who’s the target audience?

Roopa Mohan:
The target audience here was initially the ISVs, because they’ve been forced to go through security review and usually that happens when they are nearly done writing all of their packages. So they’re ready to ship and that’s when they find out… If they’re an ISV for the first time, that’s when they find out that there is a security review process. And then once their code gets reviewed, they get feedback and some of this feedback goes back as much as modifying their design. And then suddenly their entire plan derails.
So they have to go back, fix the things that were previously unknown, and then there are multiple cycles of this back and forth going through, and by the time they’re actually done, it takes a few months. So our main idea behind this was to give them the capability to know about what these issues could be right when they’re developing code, so that when they’re actually going through the security review process, they’ve already thought about all these different things, they have ideas in their head, and it’s just much easier to get through security review at this point. So we want to empower them, we want to give them the feedback right when they are developing.

Josh Birk:
Right. So like at the end of the day they can run the Code Analyzer, catch the big rocks, and then actually kind of possibly breeze through a security review…

Roopa Mohan:
Hopefully. That’s the hope.

Josh Birk:
… Although, I don’t know if anybody does. Right. Nice. Nice. Okay. So let’s break down these various parts of the Analyzer. First, one of my favorites, and I’m guessing possibly the core of the Analyzer, but you could correct me if I’m wrong there, but what is PMD?

Roopa Mohan:
Ah, okay. So PMD… Yeah, we’ve already had a couple of episodes in your show about PMD.

Josh Birk:
Yes.

Roopa Mohan:
Yeah, my favorite episodes for sure. So PMD has been this fairly large community of developers, and then I think they’re all passionate about writing really good code, and that’s why they must have even started this in the first place. And PMD has a great community for AP and Visualforce, and this has been the go-to for many developers because it’s open source, it’s free for all, and it’s written really well. It has a lot of great rules. So that’s like a crux for how… With our product, since we wanted to do this for mainly Salesforce developers, it immediately became apparent that PMD is the first thing to consider given the support they have for Apex and Visualforce.

Josh Birk:
Right. Nice. And trick question, what does PMD stand for?

Roopa Mohan:
Nobody knows that. That’s… It doesn’t have a full form, right?

Josh Birk:
I know. I love it. I love it. And now it has a counterpart in ESLint, and that’s for JavaScript and LWCs, is that right?

Roopa Mohan:
That’s true. Yeah. So ESLint has been another great open source static analyzer out there for all of JavaScript. They have a variation for TypeScript, and then there is one more… LWC. So they have all these really cool ways to plug in different layers of parcels and different structures of JavaScript so it can transpile and then look at different languages that are flavors of JavaScript. And that’s been really handy for us because a lot of our developers are in LWC and we want to be able to support the code that they write. So that became the automatic second engine to be inducted into the Code Analyzer’s backend.

Josh Birk:
Gotcha. And I do kind of love starting here because these two tools that we’re talking about, well first of all, now easily wrapped up within Code Analyzers, so use that. But we’re talking about two tools that every developer should have in their toolkit. This is something we all should really be… It’s so easy to run and it can catch things so easily and fast. When I interviewed Robert, PMD kind of ran in a very specific way with specific rules. How customizable are these things at this point?

Roopa Mohan:
Oh, okay. So PMD is pretty… So both of these are customizable, super easily customizable. They have really good documentation in their websites that even have starter rules, and then we can go on building on top of that. So let’s say if I have a team and then I want my team to follow a specific naming structure for how classes are named, I can probably create a custom tool just for my team and then we can have it in our autobuild and get that going. So it’s the same for ESLint as well. Everything is purely configurable and light for them.

Josh Birk:
Nice.

Roopa Mohan:
The challenge here though is, for someone to know both of these in detail, they have to be a power user. They should have had a lot of experience using these tools and then set it up, get it running, look at the output. The output is different formats between ESLint and PMD. And it’s just, at some point, if we’re to set all of these things up, it becomes really [inaudible 00:09:09]. That’s kind of what we are trying to avoid with Code Analyzer.

Josh Birk:
I was going to say, so does Code Analyzer provide a different layer so that you can talk to them more easily and customize them more easily?

Roopa Mohan:
Kind of, yes. So with Code Analyzer, the user does not have to know that there is PMD running in the backend, there is ESLint running in the backend. All they know is that Code Analyzer can be installed, just like any other SFDX plugin. It’s literally a single line. It’s like, “SFDX plugins install.” That’s it. So it’s just a single line, and then it automatically gets all the PMD, ESLint, and the other engines that we support within their system, and then they just have to interact with the single tool that’s going to give the format of all the different output in the same structure.
So let’s say if I were to use this output, and then pipe it into another tool for my team so they can look at results in a different way, they can easily do that. There’s like a JSON output, or an XML output. Or if they wanted to look at it in a spreadsheet, get a CSV and open it in a spreadsheet application. That’s it. So I think the value here is to allow the developers to focus on their development rather than setting up the tooling needed to write good code.

Josh Birk:
Got it. And it gives them one thing they have to learn as opposed to one, and two, and the other things that we’re about to talk about. It’s just know the interface for Code Analyzer and you’re done.

Roopa Mohan:
Exactly. Exactly.

Josh Birk:
Nice. So let’s go onto some of these other engines, new in 3.0, Code Analyzer 3.0, is support for RetireJS. What exactly does that do?

Roopa Mohan:
Okay, so RetireJS is another open source tool as well, and what they do is… So if we have JavaScript code written and then we have dependencies on other JavaScript packages that I found online… So yeah, that’s kind of the cool thing with JavaScript, and Node.js in general. We have all these cool things that community has written and then you can directly start using it. The value that RetireJS adds here is that it makes sure the dependencies that we have don’t have vulnerabilities. So for example, that could be a jQuery version, an older version that’s known to have a security vulnerability, and then a newer version of jQuery fixed it. If it notices the user’s code having a reference to the vulnerable version of jQuery, then it’ll throw a violation saying, “Hey, I think you might want to update that to the latest because the version you have has an issue.”

Josh Birk:
Gotcha.

Roopa Mohan:
So it goes one level further. [inaudible 00:12:09] the dependencies and close violations. Yeah.

Josh Birk:
Nice. Well, and I can imagine that’s probably a pretty common violation that comes up in security reviews.

Roopa Mohan:
Exactly. Yeah.

Josh Birk:
Yeah. Yeah. Does it offer any means for swapping that out or is it up to the developer to be like, “Oh, I’ve got to go download the new files, relink them,” and all of that?

Roopa Mohan:
Yeah, they’ll need to do that themselves. So in general, with the Code Analyzer, we’ve been staying away from modifying any code that developers write just so that the entire user experience is more secure. So if there was something that was unintentionally modified, that’s exactly the situation we’re trying to prevent here.

Josh Birk:
Got it. You don’t want Code Analyzer to be the thing that actually broke the code.

Roopa Mohan:
Exactly.

Josh Birk:
That makes sense. 3.0 also has a copy paste detector. Why would somebody want a copy paste detector?

Roopa Mohan:
This is a fun one. So I mean, I’ve done this a lot. I’m guilty as well. But then at some point you’re like, “Okay, I need to get this done in the next 10 minutes. Let me just copy over that block over here, then just change a couple of things here and there, and then just get it running.” And then I forget about it entirely. Things start working, I [inaudible 00:13:23], and then one month later I’ll be like, “Oh, I had this ugly thing that I copy pasted. I should probably go back and clean it up.” I never do that. And then I’m like, “Finally. Somebody remind me, somebody tell me, somebody push me to go do this.” So the push is exactly what the CPD tool does. So CPD is also from TMD. I think it’s a really cool…

Josh Birk:
Okay.

Roopa Mohan:
It goes, detects all these pieces of code, and then tells the developer, “Hey, you, [inaudible 00:13:57], go. What is this copy pasting you’re doing? Go fix it.” And then…

Josh Birk:
Right. So it gives you the warning that instead of the 15 blocks of code that are identical, maybe you could turn that into one function.

Roopa Mohan:
Exactly. Or at least just tell them. So it wouldn’t really ask them what to refactor, but rather it says, “Yeah, you have all these blocks that look like it’s a copy paste, so do something about it.”

Josh Birk:
Got it. What’s its limits? Is it looking at functions, like blocks of code, or if statements? What’s-

Roopa Mohan:
It is just stretches of code that… Just stretches of text, actually. I don’t even think it pares the code. I could be wrong.

Josh Birk:
Got it. But it’s just saying “Block, block, block. These things are all same.”

Roopa Mohan:
Exactly. “From line number five to line number 105 is probably the same piece of code.”

Josh Birk:
Got it.

Roopa Mohan:
And all these different things.

Josh Birk:
Okay. All right. Okay. Now I’m once again reminding you, I’ve given you permission to be as technical as you want, because these next two things, I think, need the heavy. Go heavy with this. Because I was reading the blog post and the material and I’m like, “Okay, I’m not entirely sure I’m following this.” Define and describe an abstract syntax tree to me.

Roopa Mohan:
Okay. Abstract syntax tree. This is going back to my compiler design class and rethinking. So if you look at a block of code, let’s say we have public class, my class, and then there’s like these curly braces, and then there’s a method, my method, which is a private method, probably, and then it returns a string and then it takes in a parameter, I don’t know, integer. And then there is a lot of code that my method does. Now we, as humans, just read this as words put together, but then the way the compiler will look at it is that it’ll have a grammar… So for example, if you are writing in Java, Java has a specific grammar. If you’re writing in Apex, it expects a certain set of keywords in certain places, and then the way the code should look for it to understand. So we can’t really expect Apex to understand code written C, because they are entirely different, right?
So how does a compiler even know that this code that we are giving it belongs to that? So we need the code to follow a certain format. So the way it will pass is it would look at all these different words, and then it’ll apply that grammar and it’ll create some kind of a hierarchical structure to say, “Here is this class.” The class’s attribute is that it’s a public, and then this class contains a method, and then this method’s name is my method, and then the return type for this method is string, the parameter list that it has has only one item, which is an integer, and then the name of this variable is whatever.
So it kind of builds the structure so we can easily get the structure as well. You can look at it as a XML, and then it’s pretty much like a tree. So abstract syntax tree is… Actually, this is kind of controversial. It’s probably a parse tree at this point, and not an abstract syntax tree. But then I’ve heard people using this interchangeably. I’m not a compiler expert. So we’ve been sticking to the term AST just because it’s easier, it’s shorter.

Josh Birk:
Okay, got it. Got it. So it’s sort of like a complex mental map of how a class should be structured in operate. Does that sound like a paraphrase?

Roopa Mohan:
So what you described would be the grammar, something that says, “This is what it should look like.” And then AST is this amalgamation of the grammar applied on the code. So it translates the code into this tree-like structure that it can understand. So it knows that, “Okay, I used this grammar, I applied it on this code, and then this is how these different parts of the language from this class.” Does that even make sense? Yeah.

Josh Birk:
It does. No, I see what you mean. It’s not just a mental… It’s a mental map that’s been applied to the code and then the tree is sort of the output.

Roopa Mohan:
Exactly. So the output is very specific to the source code that we pass in. So for Apex, if we have five different classes, all of these will have their own ASTs.

Josh Birk:
Got it. I am now also reminding myself of one of my favorite lines on the show ever, and this is episode number one with Robert, where he talks about, “I mean, how hard could it be to create an Apex PM? There’s already one for Java.” I think you’ve just described why. And his fallacy turned out, it was actually really hard.

Roopa Mohan:
Oh, I don’t even know how he made it the first time. It’s just… Yeah.

Josh Birk:
Yeah. Herculean, I think, was the effort. Now, and with the same caveats as before, how does data flow analysis compare or contrast to that?

Roopa Mohan:
Okay. This is actually interesting. So data flow analysis, it’s an animal that’s applied on top of AST. So we need as AST for data flow analysis to happen. So AST is this basic thing that we can use to even interpret that source code into something that we can programmatically go through. I think most static analyzers out there use AST as a starting point. The way data flow analysis would look at code is different from how just a hierarchy goes together. So from a hierarchy, we can derive the control flow as to how the control goes through in a piece of codes. For example, if there is a if condition, and then right after the if condition there is basis. And then we say, if this condition worked out true, this part of the code will be touched, and then the [inaudible 00:20:26] part would not be touched if the condition is true.
So the kind of control flow that each code has, or maybe there is a method invocation from one method to entirely another class, an instance… So these are things that we call as control flow. And then data flow analysis goes one step even further. It looks at how data flows through this control. So what happens to the parameter that was passed into this method? Do we even know what the value is? I don’t know. So it tries to make an educated guess as to how data transforms from one point to another.
So at any given point, the data flow analysis would be able to say, “Okay, at this point, these are all the variables that are within the block, these are all the fields within this class, and then these are all the static values that I know about. And I know that we are currently at this instance of this class via the state of all these different things are such and such.” And then at every point, if you’re doing an int++, then it knows that, “Okay, I kind of guessed that this value was a zero, the counter was set to zero, and then the value is increment here, so let me call it as one.” So data flow analysis kind of tries to emulate what it looks like if that code was executed. It doesn’t execute the code today, but then it attempts to reproduce what would happen if this code was running.

Josh Birk:
So a way of running along the tree, so to speak, but without having to actually either compile or execute the code. It’s all ephemeral. You’re not changing data or anything like that.

Roopa Mohan:
Exactly. Exactly.

Josh Birk:
Got it. And this is all being run under something called the Graph Engine?

Roopa Mohan:
Yes. That’s the newest engine. So we’ve been feeling the pain of the complexity of some rules that we’ve wanted to create. So with most of the static analysis engines out there, because they’re AST based, they can find a lot of cool things, but then they’re still restricted to whatever is within that AST. In fact, it’s so restricted that if you had multiple classes, and each of them had their own ASTs, there’s still not a easy way for understanding how AST from this class relates to AST from another class. So it’s all very, very locationally specific. So it’s all scoped very small to the file that has all of these classes. So that’s been a complexity that we’ve been trying to overcome. Because we need to know what happens when all these different classes interact with each other, it’s not always a single point that we try to find issues for. So that’s exactly when we started thinking about data flow analysis and that was the starting point for the Graph Engine.

Josh Birk:
And what are some of the new things in 3.0 that this you’re adding in gives the Code Analyzer?

Roopa Mohan:
So it’s pretty much the capability to do data flow analysis on Apex. So right now the engine is… It’s pretty stable, and we’ve added one rule that… This was a couple of months back. So we started with one rule, which is to detect when a data operation is missing CRUD FLS check. So I think Apex developers probably have felt the pain for this. Anytime you do an insert-

Josh Birk:
The dreaded FLS check. Yes.

Roopa Mohan:
Yes, exactly. And then which one needs a CRUD check? Which one needs an FLS check? And because it’s kind of like retrofitted. Older Apex developers probably know this, but previously this did not exist and CRUD introduced a little later, and that’s been… Yeah. Anytime I co the Apex, I’m like, “Oh, this check. Oh no, I have to go add this check.” So I can relate to the pain. And I see all these really well written code where developers break it into business logic and the backend logic for doing this check. It’s so beautifully written. They just pass in a string name, and then a utility somewhere else gets the string name, and then it figures out which object this is and then what kind of access checks it should have. So it’s really nicely written, and that became a complexity for us because they have so many different classes interacting. And how do we even find CRUD FLS reliably, right? So if we were to always have the line before the insert statement, that would be easy. Any agent can find that. So that-

Josh Birk:
Yeah. See, and now that we’ve walked through it, I can kind of… Because it’s basically playing ping pong with the data flow analysis because they’re just going back and forth and back and forth and back and forth, right?

Roopa Mohan:
Exactly. And then at any point an insert operation happens, we’ll need to check… Okay, so maybe there is an [inaudible 00:26:05] enabled method that’s like the entry point, and then it somehow reaches this specific insert statement that you’re looking at. So at any point across all the paths it walked, was there an FLS check made for this insert? And then is that FLS check matching the same object name? And then the same list of fields that were modified?
And then another complexity is the different fields are modified, added, at different parts of time. It’s not in one place, everything gets added. So we have this object that transforms over time. So when it finally gets inserted, it’s different from how it looked when this object was in instantiated. So, yeah.

Josh Birk:
Got it. Nice. Nice Anything on the roadmap that you want to talk about?

Roopa Mohan:
Sure. So even before the roadmap, we have a couple of new rules that are getting added. So one thing we found out was the CRUD FLS has been… So even though it’s really beneficial, it’s actually one of the rules that… So one of these violations that get identified only when code goes to security review for the first time. So until then nobody even knows and somebody has to manually go through and then find out where these problems are. So this just opens up an entirely new world of… It’s like opening new gates to identify CRUD FLS. It comes with a cost. The cost is that it takes a while to finish running. It’s pretty heavy on the resources because it needs to walk through all these parts, and things can get complex very quickly. And any complexity adds to the resources consumed by the engine.

Josh Birk:
Got it.

Roopa Mohan:
Now the sweet spot that I’m talking about. So what if we can find something more than an AST, like a simple hierarchy, but something less than walking through all these different parts? So that’s where we found the graph based. So the way Graph Engine works is that it compiles code, creates this AST and then it loads onto a graph. We’re using Apache TinkerPop, by the way, in the back end. And then we use this graph to then derive the parts and then we start walking. So we’re trying to come up with rules that can run directly on the graph so that it has some information about how these classes interact with each other, but it’s not as detailed as what we’ll get if it was run with path based. So yeah, we have a couple of rules coming up for that.

Josh Birk:
Okay. And how much of this is open source?

Roopa Mohan:
Everything is open source. It’s all out there. Yeah.

Josh Birk:
That is awesome. Where can people get started with it?

Roopa Mohan:
Oh, okay. So-

Josh Birk:
And we can have links in the show notes, of course.

Roopa Mohan:
Sure, yeah. Yeah. I think the best way would be to just get… If you don’t have the SFDX CLI installed in your local, that’s probably the first thing to start with. Get your Salesforce CLI installed in their local. And then if you happen to have your code only in the org, it’s probably a good idea to download it, have a local copy. Or maybe if you want to run it on a CI/CD system, that’s a good thing as well. Put all the code there and then just do a SFDX plugins install at Salesforce/sfdx-scanner. In fact, I think there’s a new just in time Salesforce CLI capability. So if we just run the command, it tries to understand which plugin is needed for that.

Josh Birk:
Oh, which plugin you need. Got it. That’s pretty cool.

Roopa Mohan:
Yeah, I haven’t tried it out for myself, but that sounds super cool. Yeah.

Josh Birk:
It does sound super cool.

Roopa Mohan:
Yeah. And then that’s test it. Yeah, one single install, just starts running, and then just start looking at the results. There are a couple of commands. One is the scanner run, and then scanner run DFA. Scanner run DFA is the one that will do the data after flow analysis. Scanner run can also take engine as SFG. So when you do that, you get the graph based rules. So we have a rule for unused method detection, and then there’s one more for detecting anytime we define a type, but then we don’t implement the type. Like what if we have an abstract class and it doesn’t have an implementation? That’s just code that’s not used anywhere. So it detects things like these. Yeah.

Josh Birk:
So I guess that kind of brings up my question, going back to the Voltron joke. You don’t always have to assemble all of Voltron in order to use this, right? Can you just use the PMD part of the engine or the ESLint part of the engine, and then not have that data flow analysis-

Roopa Mohan:
That’s totally fine. Yeah.

Josh Birk:
Got it. Okay. Very cool. There is another question I want to ask you, which is kind of adjacent to this, and I wouldn’t even be asking if I didn’t know one of the capabilities of this, but I’m curious your thoughts on… And I can’t believe this is actually the second episode, I don’t know which one we’ll air first, but the second episode this week I’ve taped that I’m asking a question like this. ChatGPT, which apparently can do things like… And I know, so this is sort of a sideway analyzer, because analyzer’s not writing code, right? But I’m kind of curious if you think that kind of technology, like AI technology, has a role in taking this to a different place, being able to offer comments, and complete code, and things like that. Where do you think this might go when it comes to developer tooling?

Roopa Mohan:
That’s actually a really great question. Can think of AI being used would be actually to find anti-patterns. So what if there’s some specific pieces of code that keep running into the same issue? Maybe they always hit [inaudible 00:32:22] exception, and then something. A combination of certain things happen together, and I think… So the first half of knowing something is an issue and we need a rule to identify it, I think that would be really helpful if we have a technology to put all of these together. “Okay, all of these have a common issue,” and then “This is probably what’s going wrong” and then… Yeah, that’s kind of how I’m imagining it. To use AI to detect issues would be interesting. So without the AST conversion capability, I can’t imagine how far this can go, but maybe this AI can reach that point, where it knows if it sees code, it converts that into AST. And once it has the AST, I’m sure AI can probably find [inaudible 00:33:20] left and right. Yeah.

Josh Birk:
Interesting. And that’s our show. Now, if you listen to Roopa’s original episode, you might remember she’s a fan of competitive Bollywood singing. Yes, that’s a thing. You can look it up. And so here we talk a little bit more about her singing.

Roopa Mohan:
You already know the answer. Yeah.

Josh Birk:
I do.

Roopa Mohan:
I sing a lot. I love singing, and we used to have a band. So I used to be the keyboardist in my college band. And then we had a band, a functional band, in California a few years back, and then we haven’t had time to go back to it. But I used to keyboard for a while and then I became a singer. I’m like, “Okay, keyboarding is fun. It’s too much of an effort to get all the notes.” But then I love singing, so it’s like, “No, I’ll be the singer.” And yeah, I still make time somehow, every month, to somehow sing now and then. Yeah.

Josh Birk:
That’s awesome. That is awesome. All right, Roopa. Thank you so much for the great conversation and information. That was a lot of fun.

Roopa Mohan:
Thanks Josh. It’s always so fun talking to you. I had so much fun talking with you for this episode.

Josh Birk:
I want to thank Roopa for the great conversation and information. And as always, I want to thank you for listening now. If you want to learn more about this show, head on over to developer.salesforce.com/podcast where you can hear old episodes, see the show notes, and have links to your favorite podcast service. Thanks again, everybody. I’ll talk to you next week.

Get notified of new episodes with the new Salesforce Developers Slack app.