Adrian Larson is a Salesforce developer over at Counsel. Today we talk with him about his insights into Apex development.

Throughout our conversation, we also get into some of Adrian’s earliest experiences including working with Python and what the transition from there to Java was like. We have a great discussion about StackExchange and optimizations as well. Tune in and learn from Adrian’s years and depth of experience.

Show Highlights:

  • What Adrian’s current role looks like.
  • How he got involved in StackExchange.
  • What constitutes a good answer on StackExchange.
  • How Adrian became a moderator for Salesforce.
  • What a fluent pattern is and what it can help with.
  • How Adrian and his team use micro-optimizations.
  • What fluent query and fluent syntax mean.
  • How Adrian utilizes dependency injection for performance increase.
  • Why he works out an API first and then writes rigorous tests for it.
  • The benefits of having a trigger handler class.

Links:

Episode Transcript

Adrian Larson:
Well, if you count web development, then I started putting pages together in college. My spare time bit of a hobbyist.

Josh Birk:
That is Adrian Larson, a Salesforce developer over at council. I’m Josh Burke, your host for the Salesforce Developer Podcast. And here in the podcast, you’ll hear stories and insights from developers for developers. Today, we sit down and talk with Adrian about his insights into Apex Development. We’ll swing by the Stack Exchange as well, but first let’s talk about his early years.

Adrian Larson:
So I learned the basics, HTML and JavaScript, and then worked my way up to PHP and working with databases like MySQL, a bit of SQL to set the tables up and so on. And then I took a class on Python, which was amazing. I’m a favorite [inaudible 00:00:59] for language.

Josh Birk:
Got you. Nice. What do you like about Python so much?

Adrian Larson:
It’s just really fluent. It feels like talking to a computer. The syntax is really great for natural programming flow.

Josh Birk:
Got you. Got you. Well, digging into that a little bit. Tell me about building a multi-threaded web crawler with Python and visual basic. What exactly did that do?

Adrian Larson:
Yeah, so mostly it was just reading out HTML and parsing it for data that I needed that really should have just been a public API.

Josh Birk:
Got it.

Adrian Larson:
But there were some pages that I actually needed to walk through JavaScript. Loading, just the HTML request wouldn’t give me the dom that I needed. So I actually needed to use Selenium, load up a Chrome window in Selenium and actually navigate. So I had to teach the automation tool to click through to where I needed to and then pull the markup and parse it.

Josh Birk:
Got you. Got you. What kind of data were you going after?

Adrian Larson:
It was property data. So we analyzed tax assessments on property.

Josh Birk:
Got it.

Adrian Larson:
And basically tried to find where our clients might… Arguments that our clients were being over taxed.

Josh Birk:
Got it. Got it. Okay, cool. And then how did you get involved with Salesforce?

Adrian Larson:
So, Google called me, they left a voicemail. I thought it was Google. Yep. I hadn’t heard of Blue Wolf. I hadn’t heard of Salesforce, but they found my resume and interviewed me and I nailed the technical project and the interview process. They brought me in as a green developer. They definitely believe in giving very young, inexperienced developers, a chance to grow with them or they did it at the time. I believe they still do.

Josh Birk:
Got you.

Adrian Larson:
And they offered me a job in Denver. So, that was somewhere I always wanted to go.

Josh Birk:
Nice.

Adrian Larson:
It’s a win-win.

Josh Birk:
Nice. Now at the time, had you moved into Java or anything that was Apex like?

Adrian Larson:
Nope.

Josh Birk:
Really?

Adrian Larson:
Python was as close as it got. Yeah. But, it’s not that big of a shift to move into static typing. It’s conceptually pretty easy to work with if you are familiar with object oriented programming, but yeah, it was pretty easy to transition honestly from Python.

Josh Birk:
I mean, you were already dealing with Python as you were doing threading and stuff like that. So you were handling Python as an officer, the language to begin with, right?

Adrian Larson:
Yeah, yeah, yeah. Definitely needed class structures for all of that. And I had a UI layer and I had probably five different main classes that had quite a bit of responsibility in each.

Josh Birk:
Got you. Got you. And then how would you describe your current job?

Adrian Larson:
So right now I am actually working on an internal org, which is great because I get the best of both worlds. I kind of left consulting to see how things might work in an in-house shop. But I didn’t like being the only Salesforce person. So, what I found is when you’re working in house, a lot of times, you’re one of the very select few people who actually know anything about Salesforce. Got you. And a lot of what I like working about Salesforce is bouncing ideas off people figuring out best practices should be, and having this community and a group understanding, creating a culture, a best practice. So I lost all that when I went in house. I came back to IBM and now I’m working on this internal org where we get to just focus on one project long term without all this task switching. But then we get to build out what we think is the right way to do things.

Josh Birk:
Got it. And so I think that kind of leads me to my next question, because it sounds like you’re somebody who sort of… Building an application for correctly takes a build sort of thing. So when did you first start getting involved with Stack Exchange?

Adrian Larson:
Yeah, I started at Stack Exchange… I can actually pull it up. Let’s see. I think I joined in 13. I’ve been a member for eight years and four months. Yeah. June of 2013.

Josh Birk:
Got you.

Adrian Larson:
So I started developing in February of that year and I guess that’s how long it took me to feel like I knew anything. And yeah, I mostly was reading questions during that time, trying to figure out the style and tone of the platform Stack Exchange, which definitely has a learning curve. If you post… Before you figure that out, you definitely can get flamed and really tank your reputation. So I was more careful at the beginning and just tried to make sure… I knew the answer if I was going to post the answer. Mostly I go on there, answer things, but I do have questions too. I wonder how much I posted in 2013. Not much. Yeah. I had a couple posts in 2013, but I didn’t ask many questions for those first few years.

Josh Birk:
Got it. Sort of just putting your feet in the water sort of thing.

Adrian Larson:
Yeah. More answering. I learned a lot about how to do things and how to answer other people’s questions, figuring out what are good questions asked is a little bit harder as you’re not stuck.

Josh Birk:
Got it. Well, tell me a little bit more about that. I guess let’s start on the answer side of things. What constitutes a good answer that’s going to be well supported by the community?

Adrian Larson:
Well, obviously accuracy. Understanding what this poster is asking. There is a bit of an art to it sometimes because not all of the questions are high quality or straightforward and sometimes the phrasing of the title, or even the question body itself lead you down one path. Whereas if you read between the lines, you can see that they’re actually asking something else.

Josh Birk:
Got you.

Adrian Larson:
So that definitely, there is an of that. A lot of it is just a race. The Stack Exchange. There are a lot of questions out there where probably at least a dozen users were going to log on that day will know the answer. It’s just a question of who posts first. So yeah, a lot of the reputation is about yeah, just being on there, happening to be on there during the right time. And that’s one thing about Salesforce development that actually fits really well with Stack Exchange.

Adrian Larson:
And during that time particularly was, you save this file and then you have 30 seconds where you’re doing nothing. So you can kind of scroll through and see if there are interesting questions. And then you run a unit test and you have a minute or two where you’re not doing anything and then it fails because it’s something stupid. And then you spend another 30 seconds compiling and another minute or two running a test and back and forth and back and forth. So you have a lot of dead time and it gave me a way to fill those gaps and still feel engaged.

Josh Birk:
Got it. Well that I think kind of shores up where I was going next. So you’ve got over 138,000 [inaudible 00:08:42] at this point. And so like, you’re getting a minute here. You’re getting a few minutes here. You also have 138,000 [inaudible 00:08:52] over eight years too. So what’s the overall time commitment. And is it solely in that sort of… Waiting for the unit test to complete kind of thing?

Adrian Larson:
It’s definitely not solely that. I would be lying if I said I didn’t spend any time outside of… I definitely didn’t even do the majority of my Stack Exchange interaction during work hours, that just was…

Josh Birk:
Got it.

Adrian Larson:
Helped build a habit and regularity and sort of once you see those reputation points flowing in and you get some point cap days, really starts to become a bit addictive. And so I would be hanging out after work and didn’t have much going on and maybe I’m playing a game or watching a show or movie and just scroll through while I’m doing that as well.

Josh Birk:
Got it, got it. I will note that I saw that you also have a decent amount of [inaudible 00:09:52] over in the sci-fi fantasy Stack Exchange and

Adrian Larson:
For a few posts, but a couple big ones. Yeah.

Josh Birk:
Yeah. And for the unfamiliar, that’s a stack that tries to answer in universe questions using like in universe data and I had to quit.

Adrian Larson:
Oh man.

Josh Birk:
It was too addictive.

Adrian Larson:
Oh, I bet. I don’t scroll through there very often. I sometimes will pick a tag and prove the best questions just to see what I can learn.

Josh Birk:
Yeah. And then how did you end up becoming a moderator for it?

Adrian Larson:
So for Salesforce, when I first joined, there were three moderators. The only one who was active really was Matt Lacey. And already by the time I had joined the other two moderators had backed off almost completely.

Josh Birk:
Got you.

Adrian Larson:
And Matt wasn’t that active either. So I spent a lot of time cleaning up posts, voting to close posts on. I can’t remember who stepped down, but anyway, someone did step down eventually. And so I threw my hat in the ring and won the nomination. And pretty much my behavior is the same. I would say a closed fewer because now my vote is unilateral.

Josh Birk:
Got it.

Adrian Larson:
I generally don’t review the closed queue because I don’t want just roll with an iron fist.

Josh Birk:
Got you.

Adrian Larson:
So in that way, I actually am less active in moderating activities. I can however, delete comments, which is nice. So I’ve probably deleted a few thousand comments in the last year, I guess I can even check, but yeah, it’s just trying to keep things tidy.

Josh Birk:
Got you. Got you. Well, I wanted to poke into a few of the moments that you’ve had on Stack Exchange and like there’s one from four years ago. And it’s you asking if it’s really okay to ever catch a generic exception?

Adrian Larson:
Yeah.

Josh Birk:
This is one of your top rank… I also just love the fact that it’s like… I think you asked the question around like 3:30 and then like by four O’clock-

Adrian Larson:
It was so fast.

Josh Birk:
SFDCFox Brian Fear himself like, here’s a very extensive answer for it. But talk to me a little bit about that. The question and the answer and is it okay to ever catch a generic exception?

Adrian Larson:
Right. So yeah, he actually typed up a thousand words in four minutes and hasn’t even edited it so you can tell. Yeah.

Josh Birk:
Yeah.

Adrian Larson:
He had already thought about this.

Josh Birk:
Yeah.

Adrian Larson:
My opinion is still pretty firm that I would reject a catch of a general exception. I would say if you’re writing a rest service or a UI layer that needs to be very robust, sometimes catching a general exception, you just have to do it. I still don’t love it, but I know that the reality is you need that service to be resilient. And so that’s how you do it, but still in general, especially in any sort of trigger logic or code that doesn’t have to support a vast array of used cases that generally will try very hard to isolate. What type of exception am I catching? If I have just a DML statement that I’m trying to protect, I don’t need to wrap five other lines and catch a generic exception. I can just wrap that DML statement in a tri-block and catch just a DML exception, don’t wrap too much. Don’t be too broad. Don’t cast too broad of a net because it doesn’t actually protect your application as well. It can lead to more unexpected behavior.

Josh Birk:
Got you. Got you.

Adrian Larson:
Or just hide issues. I mean, I’ve definitely seen times where… Well, especially with poorly written unit tests. It’s kind of a confluence of events, right?

Josh Birk:
Right.

Adrian Larson:
People who write, catch generic exception tends to in my experience, totally anecdotal write very good unit tests and sometimes even sweep things under the rug. So that catch block totally hid something where a test should have failed and then you get into production and 4,000 transactions later find out, yeah, you actually caused the bug.

Josh Birk:
And it’s completely invisible now.

Adrian Larson:
Maybe even several weeks later, because people just start getting frustrated, don’t understand why things are going wrong and you’re just sweeping it under the rug.

Josh Birk:
Got you.

Adrian Larson:
My philosophy is to try to keep the scope as limited as possible.

Josh Birk:
Got it. Got it. Well, and then there was some more recent one, which again, Mr. Brian Fear jumps in really quickly, I have a feeling maybe that you have common occurrence with that, which is interesting in its own. Right?

Adrian Larson:
Well, anybody who’s been on the Stack Exchange would have that experience.

Josh Birk:
Right.

Adrian Larson:
I tried to keep up with him for a while. There was a year where I actually had a lead on him for most of the year, but I am not as [inaudible 00:15:10] as that guy. He’s something else, but yeah. I think anybody who’s been on the Stack Exchange could say… If they’ve asked more than a few questions, he’s answered one within five minutes.

Josh Birk:
Yeah. SFDCFox will appear. Exactly. Yeah. On this one though, you noted you have an Axiom, anytime you would return void return this instead.

Adrian Larson:
Oh yeah. The fluent pattern.

Josh Birk:
Yeah. Tell me about that.

Adrian Larson:
Yeah. So I found out about that maybe in 2015, I think it’s in the Apex design patterns doc and someone I worked with took this tool that I wrote and basically just wrapped the fluent pattern around what I was doing and then took a lot of credit for my tool and then just mysteriously left the Salesforce world altogether. So weird story. But anyway, that’s how I learned about it. And I’ve found that it really helps with syntax. You know with Apex, you can end up with a lot of boiler plate and a lot of separate logical lines for something that really should be a one liner. And just adding that syntax where you can just dot chain through all your method calls really helps make your code base a lot nicer to read and work with.

Josh Birk:
And is there any reason not to? Like your not-

Adrian Larson:
No. I never found any. I was trying to see if there’s some sort of performance gain or loss or any sort of any downside. And I haven’t found one. I would think if someone found one, it would’ve come up by now because it is a widely used pattern for those that know it, it’s awesome.

Josh Birk:
Got you. Nice. Nice. Well, moving into some of your projects you presented at Salesforce about your limits profiler project, and you read me… Your read me for the repost starts with, “While it may be true that premature optimization is the route of all evil at a certain point, it becomes necessary.” Please walk me through that a little bit.

Adrian Larson:
Yeah. That’s a throwback. I haven’t even looked at that in years, but yeah, that’s a famous quote. I thought it would get me a little bit of attention. Yeah. Optimization is important with Apex you are limited on CPU time and it’s something we’re fighting in the org I work in now all the time, especially for us, we have these entangled domains. So you touch a case and then that touches a work order and that touches a part request and that touches a contact and that you can even have bouncing back and forth. So we were hitting a lot of limit exceptions actually, and we’ve been going through tremendous effort to resolve those problems. So fixing recursion is definitely the most important factor for us in optimization, but we also just need, need things to run as fast as they can. At one point our case object took… I think, eight seconds and 90 queries or something and we’re way down from that.

Josh Birk:
Got you. And so what is limits profiler like giving an Apex developer in order to see that kind of performance?

Adrian Larson:
Yeah. So for me it was more about answering these micro optimization questions. So I would want to say, what’s faster A or B, if I do list I’d get zero versus list indexing with this square bracket, which one’s faster? That sort of question. And when you try to use debugging to get the run time, you’re influencing the run time because having the logs on changes, how fast things run. So I wrote it so that I could get those snapshots without actually changing the data so much. I mean, you change it a little bit when you add some run time when you save the record, but we should have already taken the snapshot of how long everything took to run in the interim.

Josh Birk:
Tell me a little bit more about how that works. Is it kind of encompassing the Apex that’s going to get run, like it knows where the start is and then it kind of walks away a little bit and then at the end it adds everything up.

Adrian Larson:
Yeah. Let me pull up the GitHub. That’s been a while. If I remember correctly, there’s just a setup tear down and execute.

Josh Birk:
Okay.

Adrian Larson:
Yeah. So it’s a pretty straightforward interface. You just extend the limits profiler and then you can set up execute optionally. You can also set up and tear down data and then you tell how many times you want to run your code. So sometimes you need more granularity, right? If it’s just telling you milliseconds on one execution, you’re not going to get anything out of that. If you’re trying to figure out micro optimizations, sometimes you’re measuring things where it’s the difference between 50 nanoseconds and 150 nanoseconds. But if you’re doing that 6,000 times in a transaction, maybe that actually does matter. So anyway, yeah, you tell how many times you want to iterate and then you tell it what to do and it then it tells you how much time it took.

Josh Birk:
And you keep saying micro optimization. So, and you’re saying that there are very key things like, does dot get faster than another option. Are you pulling that into your refactoring? If you’re looking at a block of code and you’re asking yourself, if I make this smaller, will it actually be more performant or should we just leave it alone?

Adrian Larson:
Yeah. I mean, there’s definitely things that we do in the platform over and over and over and over again where we can save, even if we’re only saving few mail each time, it really adds up. The one great example is with S-object [inaudible 00:21:13] get versus the concrete field. If you know what field you’re getting ahead of time, using a concrete reference is much faster.

Josh Birk:
Got it. Got it.

Adrian Larson:
Or another famous question in the Stack Exchange, right? Is how do you get a set of ID for a less of S-object and significantly faster to abuse the map instructor and pull out the key set. And it would be to loop through and get the IDs. Little things like that for tasks that you’re going to perform hundreds of thousands of times, start your career, knowing which ones faster can make a really big difference.

Josh Birk:
Right. Okay. So moving onto another old project of yours and this might be the one that you were referring to, and I think I’m getting it now that you’ve been talking about a little bit, but what is fluent query and what does it mean to have a fluent syntax for building OutCycle?

Adrian Larson:
Interesting. Yeah, that one was just a [inaudible 00:22:01] project for fun. The tool that I had that issue with is actually object factory. The fluent query, my goal was to have one API that you could use to build all your queries fluently and got pretty far down that path. I felt like there was too much syntax still and I needed to figure out more smoothing of it.

Josh Birk:
Okay.

Adrian Larson:
And I also just felt like I was never going to use it. So I dropped it. I think if I could figure out the last few bits of Polish, it could be a really neat tool, but I just lost interest after a little while. But yeah, it gives you a way to concretely build almost everything and yeah, it caches the describes and gives you… It even has support for knowledge, data structures and so programmatically defined, it’s got enums for a lot of the different features so that you have everything strictly typed.

Josh Birk:
Got you. Got it. Got it. Well, let’s talk about your architect side a little bit. We talked before, you said you’re a heavy user of dependency injection. First, we’ve talked about it on the pod before, but let’s level set for everybody. What is the concept of dependency injection?

Adrian Larson:
Yeah. So the way we use it is basically… It’s mostly around the database. There are definitely other features where we use it to simplify our tests. But the idea is you have some aspect of the platform or your code base that you use and interact with, but you have tests where you don’t actually care about any of that.

Josh Birk:
Okay.

Adrian Larson:
So you ignore it. So as an example, probably the simplest example would be our query mocking. You just have a query class that passes through the records and then you just call query dot records. Every time you have a query in your entire code base, you just wrap it in query dot records, square bracket, type in your cycle. And then the code runs exactly the same in [inaudible 00:24:21], no difference. But then in your test, what you can do is call query dot set mark. And basically you just have a Singleton instance of the query class. Which you swap out in the test. So what that does instead of passing through what you passed in is you can preset what the query should return. And so you basically can say, here are these records that the query should return and you don’t even have to save them for the query to return them. So you don’t have to write into the database to read out of it.

Josh Birk:
Got you. Got you. And I just did to find the term for people what’s a Singleton and how does that work?

Adrian Larson:
Right. So, that’s where you have one instance of a class. Yeah. And so you lazy load it in, and as soon as you try to call some well for this pattern, at least. As soon as you try to call some static method, it will use that instance. And that’s where you define the service.

Josh Birk:
Right. So you define the query selector once and then every time you talk to the query selector, you’re talking to the same query selector basically.

Adrian Larson:
Yep. And then you can overwrite it. Critically for this pattern.

Josh Birk:
And how would you describe the performance in queries when it comes to… Because I’m assuming that’s one of the biggest takes away from this, is that your unit tests are going to be running much faster because it’s not hitting actual data interactions. [crosstalk 00:25:56]

Adrian Larson:
Yeah. So if you think about, for example, we had case taking 10 seconds run, you think about first of all, how much logic must be there for that to be the case and how many test there should be to cover that should be. So every time anyone writes any test on any of that, they’re inserting a case and then taking up 10 seconds. A lot of them were written before test set was even available. We have five different unit ads, all taking 10 seconds to insert a case that really you didn’t even need except someone queried for it. And so yeah, you had to put it in database. So, that’s an easy one minute off just in that very trivial example.

Josh Birk:
Got you. Got you. And so does that go into the factory design pattern as well? Do you have a library you to use in order to create data that is then going to be handed off by the query selector?

Adrian Larson:
It does feed into it. I think that the way it feeds into it is we actually… I’ve gone a little bit farther in this org than I add in the S-object factory library that’s on my GitHub. I made it a virtual class, so you can actually extend it and overwrite certain pieces of the functionality. And the biggest difference with this mocking structure is that you can do a dummy builder. So we have a different instance of the builder that will… First of all, always populate the dummy ID. It’ll just populate the ID with some dummy value, the increments. And second of all, it doesn’t populate any of the required fields. And the really big difference there is that the S-object factory as we wrote it, allows you to insert parent and populate those lookups.

Adrian Larson:
So if you need a parent case, then it will create one and populate it. So if you use the dummy builder, it doesn’t set up any of those related parent records. And then those records might also have required parents and so on and so forth. So you get this huge cascading effect where none of that required data is populated and therefore you just… The time savings is huge.

Josh Birk:
Nice. Nice. Okay. Since we’ve mentioned it quite a few times, what tips you have for good unit tests? What’s your strategy for writing unit tests and coordination with your actual code?

Adrian Larson:
Yeah, I actually, I’m not a believer in test driven development.

Josh Birk:
Really?

Adrian Larson:
I know it’s last minute to say that, but I did practice it for a while. I think it’s a great tool for learning. I think that when you’re trying to design APIs or really push the boundaries of the Salesforce platform, it’s not always the best way forward. For me, I work out the API that I want first and then I write the test for it in a very rigorous manner. And I find that works better for me. So, like I said, first thing is figure out the actual structure of the code for me. And then I figure out all of cyclamatic nodes that I need to test. I actually had another post on this Stack Exchange where I argued that 100% code coverage is actually not a high enough bar. You actually can have 100% code coverage without covering many of your nodes of complexity.

Adrian Larson:
So for me, I try to actually hit 100% cyclamatic node coverage that said sometimes there are code structures that there are just too many separate nodes. And for instance, let’s say, I’m trying to test if any one of a number of fields has changed. And it’s just an ancillary piece of a much bigger feature set in a domain change that we test functionally very rigorously all the time. Maybe I just test if one of those fields change and trust that people are keeping an on it. So knowing your testers definitely is a piece of that, but I’d say generally, that’s not something I do. It’s pretty rare.

Josh Birk:
Okay.

Adrian Larson:
Yeah. One thing I would say speaking of filtering and domain structure is that when writing tests and writing code, that should be testable. One of the biggest violations I find is people putting their filtering logic in the middle of their action. So if you have something you need to do in your trigger, you’ll see an if statement buried halfway down, that we weeds out certain records, and that should have just really been the first thing you did is figure out all the records you should process and have that be a separate method. And then answer the question of… So you answer first, the question of which records process, and then the question of what to do with them.

Adrian Larson:
And having these separate methods makes it much, much, much easier to test because testing, the filters, all you care about is that setup and you can just test how many records did I get back? And then usually I just do all or none. And yeah, so each test just is one filter condition. So you have the happy pass test and then one test for each failure condition. And then when you’re testing the action, you don’t care about the setup at all, as far as filtering, and you can just test what does it do? And so sometimes you might still have conditional behavior in there, but you know that, that method is always going to be passed, filtered results. And therefore you don’t have to worry about that.

Josh Birk:
Got you. Got it.

Adrian Larson:
Hugely simplifies.

Josh Birk:
Got it. It sounds like you live in a world of triggers. Do you have like a favorite design pattern when it comes to how to divide up the classes to handle the trigger?

Adrian Larson:
Yeah, I just have a… So my domain structure is typically just have a trigger handle error class. You pass in the new records and old map and set them as instance properties on that, and then you just have public void methods for each event. So I’ll have public void before insert, before update, et cetera. And I also add a… This is one of the few places where I actually support these of test visible is, I always put a test visible bypass trigger flag on every single trigger handler. And that way, if you do need to hit the database, you can do so without running all of your Apex. And so if dependency injection doesn’t work or you haven’t set it up, you can still insert records without incurring all of the costs, but it normally would have to put those in the database.

Josh Birk:
And that’s our show now, before we go, I did ask after Adrian’s favorite non-technical hobby and well, it’s a bit of a lofty one. Just not the way you might think.

Adrian Larson:
Non-technical hobby. I would’ve to say climbing mountains.

Josh Birk:
Nice. Where are you right now? And do you have mountains to climb?

Adrian Larson:
I left Colorado because I moved here with my now wife.

Josh Birk:
Oh, nice.

Adrian Larson:
Yeah. Good decision. It does hurt my hike ability.

Josh Birk:
I want to thank you, Adrian, for the great conversation and information 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 links to favorite podcasters. Thanks buddy. I’ll talk to you next week.

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