A masterclass in technical masochism that elegantly deconstructs the nightmare of modern email infrastructure. Rick proves that true engineering depth lies in rebuilding the foundations we all take for granted.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
I built an email server from scratchAdded:
Hey everyone, my name is Rick and a few days ago I saw this um video of uh Vasilious um where he uh went into detail of what he did at Atlassian. And um I thought it was um a really nice video where uh he went into a lot of detail in how they built things or and then actually what he built uh in his time at Atlesian. And one of the things that triggered me was that um it contained a lot of technical detail which is really fun to hear about and which you rarely rarely hear directly from engineers.
Um and I'm building an email platform um and the name of this platform is uh cir um and um uh I'm building this for uh over a year now. Uh and basically it's built from scratch. So all the all the components that are needed to run email I built myself um and I hosted in Europe uh and and one of the reasons to start with this is actually that uh well there's basically two big players to run email on right so it's Google and Microsoft and there is no no great alternative from my perspective. Um so that's that's what I started with. Um, but it was also a way to exercise my perspective on what is uh how does email actually work and um and this is actually the part that I wanted to spend time on in this video. I just wanted to try because I usually post uh Dutch videos uh on LinkedIn sharing uh progress of the product and those are mostly uh pretty short uh videos uh because of course they they cannot be very long on LinkedIn and people have shorter attention spans. But I actually wanted to try to uh spend more time um and then just going into a little bit of detail actually technical detail of um of the um SMTP part of of CREX because I think that's a lot of fun actually to to take you through a bit of the pipeline.
Uh there's actually quite a lot in there um that I actually didn't really think about before I was considering building an email platform. But uh like if you if you sum it up, it's quite a it's quite a big chunk of uh of things.
So this is a different format. It takes a bit longer. Uh I'm going to try to add some chapters to it so you can actually jump to something that that you might find interesting. Um so so maybe we should just dive in. Um um well maybe before that like high level uh on high level what kind of components uh do I have um because I I didn't start with with my own client. So this is what the what the client looks like. This is a web client. Um and this sits on top of my own API and synchronization engine to make sure that this is all really fast. But I didn't start with that. I started with um handling inbound email and um exposing an IMAP server. Uh so you can basically use that use uh well any uh any app um to connect. So here you can see that uh I've connected my um my Mac uh to sirx uh and it worked with with mail with calendar with reminders and contacts um as well. So all of that is in there. So it's IMAP, it's called offaf, it's card defaf um and SMTP. So I started with that and then um just to launch something fast uh and make sure you can already use it and now I'm actually adding more and more uh tools uh on top of that and aside of that because I think ultimately that those clients is still what we want, right? So you want a nice integrated client uh that you can actually um rely on every day and that also works really well together and works well on mobile. But this what I started off with. So in terms of the the so I can actually just sketch it a little bit. So if you if you would um look at the architecture of things it's um uh it is roughly um let's name this the um back end and then we have uh let's do some storage here. So this is the uh I'm using Postgress um and then um I'm using um object storage here and then the back end talks to to this database and it talks to object storage and for now I'm keeping the setup really simple. So you have a back end and the back end um it works with Postgress. I I'm also using search in Postgress. So I only have uh have that and then I'm using um using Reddis um for um for um for jobs and queuing and stuff like that.
Um the back end is written in Ruby uh not Ruby on Rails. So it's plain Ruby. Um, and I'm using Sinatra as a framework.
And this, uh, this actually works really well. It's it's it's it's performant.
Uh, I know exactly how it works. I I work with worked with that for a long time. So, uh, I know my way around. Um, and it's really flexible um, without doing a lot of magic. And I think there's a lot of value in in in reals.
Um but what I personally never really liked is that it's so uh there is so many um things you need to uh know of at least when I was uh looking into it uh maybe over a decade ago or something like that maybe it changed now but there's a lot of magic in there a lot of assumptions in how you how you approach it um that's what I didn't want so the back end is mostly that it's it's actually only JSON API um that is actually offering that so and then on top of that was basically part of the back end. There's nap server and there is the NTP server and basically call uh card deaf are subset of the back end, right? So those are just endpoints uh that live in the in the back end. These are separate processes, right? So you need to have a way for clients to connect to to these servers.
Um and then this all runs in um this basically is my cubits cluster. So this all runs into one thing and this is currently living in Frankfurt.
Um I think roughly those are the bigger components and then there's more to zoom into there. But today I actually want to zoom in on the um on SMTP because SMTP as a as a process it's really elegant because SMTP has so many like retries built in and ways to um to make sure that uh well email actually arrives um which is which is a lot of fun. Um so um like on high level of course what happens is uh SMTP um uh ultimately delivers uh or receives or sends email um u but before the email actually goes from the SMTP and ends up in the database like there's a lot of different steps um so maybe maybe it's good to split it out um between three kind of flows that that that is fun to actually um dive into. So let's start with with inbound. Um so inbound is snp.co.
So an email arrives here and then um well uh there's basically two kinds of inbounds, right? So there's um uh and inbounds basically unauthenticated email because if you're authenticated um then um uh the biggest like the biggest reason for authentication is to be able to send uh emails to email addresses that are not part of this server. Um so inbound is basically an email that comes in that is addressed to one of the folks that is actually part of uh of my system. So the very very first thing that it actually does is um is the recipient validation and basically recipient validation means uh does it have a valid domain? Uh is a domain uh verified? So um if you connect your custom domain to Serox, you need to make sure that there's these DNS records in there. So um so I know that you actually own the domain. Um whether you have a subscription, whether you have enough storage, those are kind of the first very first steps that are taken here. Um and then the second is um is fire scanning and I'm using Clam AV for that. So there's a lot of stuff that I built myself, but virus scanning was one that I thought might be nice to uh to leverage some stuff that's already there. Uh so clamm is actually a separate uh process that also runs in my cubernetes cluster. So it's just another pod that um that the app actually connects to. So the process the SMTP process where you as a server connect to um so that runs on port 25 and then so the inbound server connect um uh the server mentions okay I'm going to deliver to this recipient we're validating that and if that's all okay then the data is delivered then we process the uh virus scanning and then we just save it into the save into spool And the spooler basically it's very simple. It's just uh a set of um uh it's just on a disk. So I have a disk attached to the to the SMTP node. Um and then each email is dropped into that folder into one of those folders as as quickly as possible. So the only thing that runs in between is is the virus scanning because I want to do that as early as possible.
Um I I could I could take that out even.
Um uh you could you could also do it like this. Um and then do the ver the virus scanning um um yeah very quickly after that or the first thing that's in the spool. But the reason for doing it like this is you want to basically have a super light and really fast process of doing uh of doing this. So I don't want to write into a database. I don't want to write into an op into object storage.
Um so it's just the validation scanning and then saving. So after the saving is done like this um this is basically when um when the client gets the okay already, right? So it's saved the file is written into the into the the disk which is local. So that's really fast.
And then the client gets his okay and then um and then we're done. And then the next section is actually the um uh the spooler has a um it's it's it's very simple. It has a loop and the loop checks are there files uh in the two in the two process folder and then it picks up each raw email file. Um so this is like an email file basically. Um and then the spooler is going to pick up um new files and the and basically what's what is happening here is that uh it is going to uh create an inbound email object. So that's very one of one of the first things that it's doing. is creating an inbound email object which is basically one object that is in the in my database. Um so I have inbound email objects, outbound email objects and actual email objects uh and some more stuff around it. And the reason is that of course inbound emails can go into multiple mailboxes. So um um and and you want to uh make sure that uh and at each step there is um uh there can be stuff that goes wrong, right? So what I want to do is save it as much as as early as possible and then start to process it and then gradually it will trickle down into the system and it ends up in the right places. So it creates this object and then it schedules and inbound email job.
So this part happens in the spooler still. So this is the part that's still in the spooler. So you have to drop off in the spooler and then the spooler is this loop uh that processes each email file. It saves it um and then it schedules this job. Um because part of creating this object is also making sure that it lives in object storage and the reason for doing this. So you first create this object um and um and then I'm copying this this email to object storage um encrypted um but um this can go wrong if the database is uh if something is wrong with the database. This can go wrong if something is happening with object storage. This is pretty fast, but can also be slow depending on the traffic.
This uh is also pretty fast, but uh of course, yeah, I'm I uh I don't know if it's up. Um it is up most of the time.
It actually works pretty well and it's really fast, but it can also be slow.
So, you don't want to have this in the main in the main flow, right? So, you want to make sure that this can all happen in the background. Uh however, you don't want to do the whole processing of the email because there's a lot uh of stuff that needs to happen.
You don't want to do that in this spooler. So then like the the the rough high level, let's sketch it because then it's clearer. So you have the S&P that um and then you have the spool um spooler uh loop basically.
So in spool spooler loop and then you have email job.
So on high level we are here right. So the server and there is um a hard line between between those as a as a client. Um this can all run asynchronously. So that's really nice.
Um so you keep the client stuff really fast. And this is also really lightweight. And then here it doesn't really matter how long it takes actually.
Okay. So now we are there. We have the um we have the job scheduled. So we are going to uh to the third phase of this and that's actually when it gets uh fun.
Um so we have the job and the very first thing that it's going to do is is run the spam filter. Spam filter I wrote myself. Um and uh spam filter is combination of uh uristics and reputation.
Um and we'll see the reputation again in a little bit. Um so it runs a spam filter and basically it returns to me uh a score uh of uh of the email and then if we determine that is spam yes or no.
And we do the reputation tracking. So for each email that comes in um uh I know which mail server it's sent uh delivered to us, the IP address of the mail server that delivered it to us. Uh and of course the envelope, so to who uh like from um uh the the from part of the email. Um but if you talk to a mill server, you also have to specify who you are. Um and basically those all those signals I combine. um and for each of those signals and tracking how often we've seen them and um what the ranking is of that um uh of each of those. Um so we track that after the spam filter is um is done and then we do a little bit of um dduplication.
Uh and that is because for some reason um there are mill servers that are um sending the same email multiple times via multiple channels in order for it to I guess arrive faster or maybe skip the grrey listing. Some servers do that. So you deliver it one time and then basically it says okay I want you to to deliver it another time in order to um to skip some spammers. Um so I there is a little bit of dduplication going on here to make sure that um you don't get duplicates if uh sending email servers um attempt it. But this goes really like it's it's quite high traffic and it goes pretty fast. So you need to make sure that um all of this is uh sort of in sync to make sure that uh that we don't end up with duplicates. Well, and then if that is done, we're going to uh resolve mailboxes.
Um, and as you can imagine, um, um, one email can go to a bunch of people. And for each of those folks, uh, we need to resolve the mailboxes. Um, and there's one more optimization that I do, and that is that I'm saving the raw email only once per workspace. So, what I'm doing is let's say you um uh let's say you email to um um um to one ata.com and one or two at a.com and three atb.com then a and a.com like is basically one is probably one workspace, right? and b.com uh might be a different workspace. So in this case, we would save the raw email uh for this workspace and then the raw email for for this workspace. And the reason I'm doing that is that I encrypt uh each email um with an encryption key per workspace. Um so um so that's why I need to save it twice. And the reason is that if you delete your workspace and delete those keys, um those raw emails from this point on are no longer readable uh by anyone. Um which is a really nice uh nice feature. But then we have to resolve them and then group them uh and then make sure that they all are um are all valid.
Okay. If if we are at that point then we have um we are saving the email the am the email and then the email um is also in object storage um in object storage because those can be large right so uh those are um well if they have attachments. They can be megabytes. Um 25 most is most is the limit that most providers use. Um and so I do that and plus meta data in um Postgress.
Um so I'm extracting some stuff uh like subject and snippet and stuff like that that's in Postgress and then the raw object is in storage. But what we also need are separate um attachment objects.
So I'm also extracting those um separate attachment objects and they are also stored separately. And this is something that I don't know I'm I'm a bit liberal in terms of uh how many different attachments I'm saving or different files. I there's actually quite some duplication already. You can see that because there it's still in the inbound.
Um, so we have it here already, right? The full file. Here we're creating some copies and here we're creating more copies. Um my thinking was like I' I' I'd rather focus on getting the pipeline to be fast and um uh and really solid and then la later uh it's uh it's uh something that we can actually optimize in terms of how much um disk space it is using. Object storage is really cheap.
So um yeah, I don't mind that too much actually. I I I prefer something to be um to be really fast and intuitive as a user. We need those separate objects because of course um in our own client we want to be able to offer those attachments separately um and also offer them in line or offer them to download.
So that's why we need them. Um you could also um for example uh do it differently and always fetch the full email. Um, but if you need to render an inline image, like let's say a small image that's part of the signature of a user and it also has a large attachment, you would always fetch the full email from object storage and that's really not what you want to do. So that's why I'm splitting it up.
So now we have the email stored, we have the attachments stored.
There is something else that needs to happen and that is actually uh resolve threading. Um because threads are not a native cont concept to emails or um actually like the uh you have this in reply to header um and um you also have the um uh the other field which is do I remember that I think it's the um well there's another field that collects the um also relevant uh emails. So there's basically two fields.
Yeah, I'm I I forgot which field it was, but there's another field. And what I'm doing is um for each email, I'm checking if for this inbox there's already a thread um that holds this email. And if there's no thread, I'm creating one, a fresh one. So then if there is a reply coming in to this uh email um that we already have then there is this spot in the process that actually is going to look up the other um email finds the thread and then adds the new email to the thread. Then we have uh also fun uh in you can snooze emails. So in the in this flow there's also the unneoo because um and snoozed email has a label removes snoozed label on reply. So uh if you snooze an email uh let's say I snooze an email for two weeks ahead and then uh somewhere earlier uh a new person replies you want the snooze label to be gone already it it will pop into your inbox and it will not return later again right so that's where the unneoo uh step comes in um and uh this is this is a small step but the next one is um actually uh very important and also a lot of fun, which is extracting HTML.
Now, you could say, "All right, but the email already has an HTML part to it, right? So, that's part of the EML file.
Why would you actually do that?" Well, turns out that that HTML is not really something that you want to render in your own client. So, if I would go to this, it's a demo inbox. So if I go here and then open an email, this part is actually HTML.
But if that contra cons um contains um images or inline attachments or if it um has um if it refers to external CSS or if it um contains CSS that basically make it break out of the window. uh we need to tackle that. Um so that's what happens here. So there's a separate step that does that. It it gets the um gets the HTML. It uh replaces all CSS links images um and stores separately in object storage. So we have another file in object storage which is basically the HTML and the HTML we only use with our own client. Um uh I don't use that in um via um IMAP because in IMAP usually those clients just request the whole file uh they just request a raw uh file and they handle all of that uh other sanitization themselves. Um that's what I'm doing. so far. I could also start to replace it, but the format is pretty finicky. So, what you um yeah, you don't want to break it. I saw that like every other client breaks on something else.
Um so, it's it's pretty tricky. So, that's why so far I decided to not touch the the raw files over IMAP, but I'm doing that for my own client because of course I control that. Um so, I'm I'm doing that. I'm also for example removing the outer HTML and body tag because that's what I don't want to render again in uh in my own client.
Um so we have the uh HTML extracted now.
So that's fun. Um and now there's a small step that is a web hook trigger web hook trigger or like an event. So um uh I haven't uh opened it up publicly but Circ has an web hook system where as an as a developer you could actually build stuff on top of the web hooks and this is the point in time where I trigger this web hook event. So you can build on top of that. Um and now we get to the um process the email filter rules.
So as a user you can create multiple filters. Um, so that's in here. If I go here, settings and then mailboxes and I go into this, I can actually do filters and create one here and then so I can basically well create my own set of things here. Um, this this is the first selection and then define some actions that should happen to to each email. Um, and that happens in this uh in this step. Uh, but only if it's not spam. So not spam uh email filter rules and the other stuff will actually happen for spam because of course we are also rendering spam in the app. So we need to extract HTML even for spam. Um uh web hook triggers is something that I'm still a bit on the verge. I I I can actually see that some platforms would want to have web hook triggers for spam as well.
Basically get the email and then get the the the relevant um labels. So you could build on top of that. But I can also see that you want to basically set up a web hook that just ignores all the spam altogether.
Um and then we have a final step here which is if it's not spam, we're also going to process calendar invites, updates, etc. Um, and all of the all the actions that you have here um that create objects also trigger websockets plus um change log um to update clients because the both my own web client and as well as the IMAP client they moni they monitor the change logs and if there is a new change log for an object then they will actually push that change to um to the IMAP client or to um to the circ client.
Okay. So that's the whole um inbound. So this is the inbound directly to the to the to the SMTP server um which is uh which which is already uh quite a lot of fun. uh and I did it in in a bunch of passes, bunch of iterations. So, initially I didn't have this spool part. Uh I did not have a lot of the things in here. So, I started really basically and then expanded over time. And I can definitely see that there might be some more ways to to make this more lightweight. Um but um it's also not completely necessary because one uh SMTP has a lot of um retries in it already and um I also have a secondary MX. Um so let's just quickly sketch that out because that's basically the fallback. So there is um secondary MX which basically they've said secondary MX.exmill.com so a different domain um which helps to keep things separate and this is in a different data center as well. So this is now uh somewhere in Austria.
Um and um this was also fun to build because it has um a bunch of the things that are also in the inbound.
So this is also 25. By the way, here we also support more ports but not but only for the authenticated uh for the authenticated flow. You can actually connect directly over TLS.
um it will do.
So the secondary MX actually what it does is it has a job sync uh list of domains and mailboxes every few minutes because what we want to make sure of is that this secondary MX is reachable.
um and can accept email even when the whole system is down, right? If the database is down, if um object storage is down, even if the other cluster if the other cluster is down, then this should still work. Um and it does. So, uh but in order to do that, I don't want to accept any email, right? Uh because that will be a bit dangerous. I mean, you could do that, but it's also very easy to flood it then. So what I'm doing is every few minutes I'm fetching a list of domains and mailboxes from the from the uh main cluster. Um so that's then synced and then we can do a little bit of light um uh light um acceptance checks here.
Um what what I actually want to do is here it's uh this one is a bit broader right so for example um um we could skip some parts of um uh the domain validation checks because what I what what is what this will actually do uh ultimately um is well first it will persist into spool it has a separate spool so you drop it off here persist into this pool and then um we have a like let's let's draw this line here. So we have a line uh so the client now knows okay it's done and then we have here the spool spooler uh loop and that will forward to primary that's the only thing it does. So the only thing this does is it loops over the unprocessed emails and then it will basically check is primary up. Uh if so it will actually drop it off at primary and then of course it ends up here. So it will do the whole flow here. Um uh it it's not different. Uh the only thing that it will do is attach a little bit of metadata so I know um at the end of the at the end of the the the whole thing where it came from. So if it initially was dropped off at the secondary or not.
Um yeah, so that's a secondary. Um which is also really fun to build. Um and actually the code is shared. Um so there's a nice little bit of abstraction that the server knows whether it's running as a secondary or as a primary.
Um uh and I'm trying to merge the code as much as possible to keep it in one place. So all the validations are are the same. But there of course there's some slight um uh some slight differences here and there.
Okay. And now the final part is um also a lot of fun because that is outbound of course. So we have inbound, we have secondary um that's all for processing and then we have outbound.
Outbound and outbound happens on the same server. So SMTP.go Let's go.
Um, but then authenticated, right? So that's the first step.
You authenticate and then from that point on the server knows, okay, you're authenticated. So that means that you are um allowed to send email to uh domains that we don't own, right? So that are not part of the server itself. But for that to happen, you need to have an active sub and you need to also um yeah, so that's part of the authentication. Then the first step we're going to do is to um check outbound rate limit because what we don't want to have happen is that you connect to SMTP and then start sending hundreds or thousands or tens of thousands of emails and then basically blast my whole reputation to bits. So that's the first thing that we do. We check if you are actually eligible to send some outbound email. Um then we also do the virus scanning for outbound and then we ignore it and drop it if uh there happens to be a virus and then we're persisting into spool just like um just like the um the inbound. So for outbound we have this part set up here.
So now we're in the spool. Um, and then we have our old spool loop.
And what this will actually then do is also fun. Um, again this is trying to keep this very light. Um, but what we'll do is validate the recipients first. So and validation means that um well uh email can be broken in lots of ways. Uh it can be empty strings. It can be completely weird email addresses that are part that are put in there. Um uh well there's number numbers of ways that this can this can break or go wrong. So that's what we're doing here. Um and then we have um also pretty essential step u which is removing PCC um and that's something that uh is fascinating because you drop off the the um email file um and basically what what happens is if you talk to SN an S&P server you're saying I I am this person and I want to drop off email to a list of people. That list of people is not necessarily the same list of people that is actually in the EML file. Uh in the email file, there can be different sets of names in there and email addresses.
They don't necessarily have to match. Um and um this also how BCC works. So if you create an email uh and add a blind carbon copy, so someone that should not be visible in your mail client, you can still drop off emails to that address of course because you need to send it to that person. Um but not all clients are handling the BC correctly as in they keep it in the file. But we don't want to have that leak out. So there's a separate step uh manual step to make sure that that doesn't happen.
Um, and then I'm doing a step to uh validate sender for workspace. Um, so as you can imagine, well, you don't want to have someone spoof uh an email address, basically saying that they're someone else. So I have a step here that validates whether the email address that you're trying to use is actually one that's valid for your workspace. So, um that it matches up and then it creates the outbound email object and recipient object per recipient because what we want to do is we track per recipient if we could actually deliver it and then this adds it to the queue another queue which is nice. So this is the what spooler does. It processes it creates another object and as you can imagine the object uh for outbound is now also persisted into object storage.
So basically that's happening here in between outbound email to object storage and then it cues it. And then we have uh another phase here which is then of course the outbound sender service. And what this will do is first it will sign the email with daycim and daycim is a way to make sure that uh we can or like as a recipient I can verify that the person that owns the domain is also the person that sent the email um with uh with a set of keys basically. Um so that's the first thing that we do. So basically if I send some something from a certain domain, we get the key that corresponds to it or we check if there is actually a valid key on the domain uh we sign the whole email with this uh with this key and you need to make certain changes to the actual email file. Um and uh that's a very essential first step. And then we are grouping emails per domain because we need to start connecting to email servers now uh external email servers. And uh one optimization that we can do is we don't have to do it for each recipient separately. We can actually uh deliver um one email to multiple recipients in one go which is a lot faster. So we do that. So we group emails per domain and basically say okay um if we are writing one email to five people and two are on the same domain uh we only need to make two separate um two separate requests. So for each group, we're going to do a DNS lookup uh to find MX records.
Um and you get a list, right, based on priority. So that's the list that if you um if you create your own domain uh and manage it and manage your own DNS, you add a bunch of MX records to that and you give it the priority. And then here basically I do the other way around. So, I fetch those records and then sort them by priority and I start with the with the the lowest number and I work my way down um to um to basically connect and try to deliver the actual email.
Um and uh that usually works but sometimes it does not because you have um an like most of the time it works but then there is a scenario of okay this email can never be delivered so in the 500 range or in the 400 range error uh and that means that I can actually retry it later. So there's multiple options here. Um so it will actually um uh store results and um we try logic if needed. Uh so this would basically then reschedu itself um at some point right so uh with um uh with some kind of a back off. So, um it first it tries it pretty fast and then if that doesn't work, it will try it again, but then with a little bit more of a delay and then try it again and again, uh until about 48 hours, um to make sure that we um spend as much time possible to see if we can get it to deliver. If at some point finally it doesn't work uh it cannot deliver, we are sending delivery notifications if needed.
So those are those emails that you recognize, right? So it's like this person doesn't appear to have an active mailbox or it's full or whatever. Um so that's also something that we have to tackle ourselves.
So that's the whole process. um of outbound. Well, I see that I actually dropped the uh the rate limit marking.
Uh that's also happening if it's uh if it's accepted. Of course, we know that you are sending uh sending an email. So, that's the whole outbound and um um actually like actually the part to like the retry logic it was fun to uh to work on and to build. uh there can be uh there's actually quite a lot of complexity behind uh the the dayam signaturing that needs to happen um and also the same same goes for the like the spool. Initially I did not have the spool and then there were cases where someone tried to to deliver to um to the to drop their email to the SMTP server and then this took too long and if this takes too long your client is going to complain. um and basically say, "Hey, uh I could not uh schedule this for um for delivery." um but the nice part is if you do it like this in line, you can also give immediate feedback, right? So then you don't have to do the the delivery notification because then you can uh return that value right away here at the top. Um, so the more you start to split up, the more you also run into cases where you need to do some async um async delivery notifications as well if that's needed.
I hope this was fun. Uh, this is the this is the outline of it and um yeah, if you have any questions on on how this works or suggestions on how to how to make things even better, then of course I'm all ears. Um, and if you made it this far, thank you for uh for watching.
And um yeah, if it's uh if it's if it's useful or fun, I could do uh I could do some others on some different topics, but um let's see. All right, have a great day.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 views•2026-05-29
🚀 BCS613C Compiler Design | Module 1 to 5 Schema Evaluation 🔥 | VTU 6th Sem 💯 #VTU #bcs613c #exam
Pranavaa-y4y
104 views•2026-06-02











