Cloudflare TV

💻 3 Ways to Simplify Your Workflow with Cloudflare Workers

Presented by Jon Kuperman, Mike Sherov
Originally aired on 

In this Full Stack Week session we will show you how to simplify your development workflow by using Workers to quickly build solutions that would have otherwise required you to buy from a third party. This includes using Workers for geolocation, image optimization, and bot detection.

Visit the Full Stack Week Hub for every exciting announcement and CFTV episode — and check back all week for more!

English
Full Stack Week

Transcript (Beta)

Hey, everybody. I am Jon Kuperman. I'm a developer advocate here at Cloudflare and I'm joined today with my friend Mike Sherov.

He's a VP of product engineering at Skillshare.

Hey, Mike, how are you? Hey, Jon. Thanks for having me. Happy to be here.

Yeah, thanks so much for coming on. I really appreciate it. I'm really excited.

Do you think you could start giving folks just a little high level of Skillshare, like what you are building over there?

Sure. Skillshare is a online learning platform where you could sign up and join classes that are mostly videos.

And it's like active learning.

So you expect to take out a pen and paper or whatever the project requirements are and do a project alongside the class and upload it when you're done.

And so just really big into it's similar to like an egghead, but for the creative space mostly.

Yeah, that's really cool. I always browse it and I've been meaning to like, I should do some like writing or some drawing stuff.

That'll be really, really exciting. And then you all use Cloudflare Workers, right?

And then do you use any other Cloudflare products at Skillshare right now?

Yeah, we use Cloudflare Workers and then we just use the entire Cloudflare Enterprise suite.

So just like all the buttons like at the push that just give us free cool stuff is like I say, it was what I always say, the suite is.

Yeah, yeah. I always like view it like an awesome shopping trip where you're like, you're going through the dashboard like, I'll take that.

Like, I'll take some image optimization.

Like I'll take some Argo smart tunnel like that. It's really exciting.

Yeah, just the other day, we got AVIF image support automatically on all of our uploaded content.

So that was nice. So good. Yeah, that's awesome. And then, yeah, I like seeing it.

I don't know, doing things that like the DNS level is so exciting because it's just there's no like code change or anything.

It just like goes out to all of your bundles or all of your images or something like that.

It's very cool.

So like when we started chatting, I think we had this cool idea that we were riffing on together, which is like improving workflows or like improving, like reducing the amount of work you have to do or like using workers, like using some kind of like serverless at edge.

And maybe you could you maybe like describe a little bit of like how you think about that stuff, like when you would be like, oh, maybe this is a good idea for a serverless function or something like that.

Yeah. So what's really I think what's really interesting about workers and about, you have these services that are just you expect when a request comes in, you then you have to go out and get some information about the request for augmented in some certain ways.

I think a couple of things that I like to think about is geolocation, bot management, security defenses, all these things that you could just kind of are really middleware for the request that you're thinking about as like your main part of your application.

Right. Like when I go to build an app, I'm not thinking like, how do I pipe to a geolocation service?

You just want the request to come in and say this person's coming from Africa or this person's coming from America.

Right. So for me, I think of workers mostly as just kind of like that middleware layer of things that you kind of just aren't part of your core business logic that are best served and can be determined before it even gets to you.

Yeah. Yeah. I think I have like definite like vision for becoming like, you know, more and more.

I want to see it more and more for like the kind of meat of the application.

Like today we announced a bunch of cool database stuff, TCP connection and stuff like that, which is awesome.

But I also think it's just such a great way to get people on board with them.

Like some of my easiest onboarding is like when people want like a security header for an existing application and they're not looking to like rewrite their whole application or it's like, well, you don't have to do like any of that.

Like it's super easy to just like grab a worker, add a header, pipe it through to your rest of your app.

Like that's one of my favorite use cases for them. Yeah. So I think a big principle in engineering in general is like reversibility and like layering.

So like you come to Cloudflare for like what you think is a CDN and then maybe DDoS production, right, to like prevent bots hammering your servers.

And then like you say, OK, well, now I could add image optimization.

I could add GZIP compression.

I could add, you know, all these things that just kind of like come for free from having a thing in between your application and the end user.

And then to your point, then after that, you kind of can move the stack forward.

Right. So you just have incremental adoption of these different technologies.

Yeah. Yeah. I really like it for that stuff.

I think it's just a great way to get started without any major investment, no rewrites or refactors or anything like that.

And it really helped me.

I know you've done like a lot of like engineering leadership before, but as like a product engineer, I think it was like a big like aha moment for me when I thought about layering my application that way.

Like I had never really thought in terms of that before, whereas like I always thought, well, you know, if you have like a Ruby on Rails app and you need to add a header, like I would always reach for the Rails app.

Like that's where I would go to add it. And then it's like the idea.

And with it being at edge that it's like cheap enough, both in terms of like what you would spend on it, but also in terms of just like time cost, like that you can really spin up and add this worker that just sits in between really clean, easy to test, does this one, you know, one thing and does it well and then keep the rest of your app running.

I think that's like a really powerful idea that hadn't really clicked with me until a few months ago.

Yeah. I would also just say that like, you know, people talk a lot about microservices and, you know, abstraction layers and separation of concerns and whatnot.

And like I just think of like from my perspective, I think of Cloudflare Workers as like a micro microservice, a serverless function, whatever it is, where like if I have a security researcher, if I have my security team and they want to instrument and inspect the application, they could just intercept the the outbound response and say, OK, does it have PII in it?

Does it have an email address? Does it have a social security number?

Does it have a credit card number? Cool. Let me go log that over, you know, via durable objects or whatever you're logging to on the outbound.

And so it's really, really nice for creating a separation of concerns that you when you're thinking about inspecting or augmenting outgoing requests or augmenting, inspecting incoming requests.

Right. Yeah. Yeah, I think that's great. So I think we had talked about and I think you had prepared like a couple of examples just like show folks like some of the like just simple ways to improve workflows and things like that.

You've got that. Yeah. Yeah. Let me pull up some code. Is that cool?

Absolutely. That'd be great. OK, so these should be fairly small examples here.

And so the makeup of a worker is pretty straightforward. For these ones, we are listening on the fetch event.

And this represents like an incoming request. So you can see that over here.

And what you're basically doing in workers is you're adding an event listener on the fetch event.

And what that gets passed is a callback with as its parameter, an event object.

And so that event object, you then need to respond to it.

So most of the workers look like most of the workers have basically do event.respondWith and you make a function called a handle request that takes in the request property of the event that came in.

Right. So this represents the incoming request to your server and you could do whatever you want with that.

So. Yeah. Sorry, this is the boilerplate basically for. No, I love it. And for folks that do front end stuff like this should look somewhat familiar to like the service worker syntax if you've done if you've done service workers on the front end.

Yeah. Or like similar to, you know, back before we had React and jQuery and all these things at event listener.

So yeah. So you basically get a function called handle request here.

This takes in, like I said, the event request from over here.

And what we want to do is we're going to create a new request that has some additional properties added to it.

Right. So you just while workers come with a runtime environment that has a bunch of built in objects.

In this case, one of these objects is called request.

You can construct a new request from the old request and do things to it.

So all requests that come in when they're inside of Cloudflare's infrastructure come with a CF property.

So that represents all the stuff that's on the Cloudflare object.

And so depending on which services you have access to, you have different information on there.

In this case, we have access to Cloudflare's geolocation service.

And so every request CF property has a list of city, postal code, region and region code.

And so you can see here, all I'm doing is adding an additional set of headers to my incoming request that are just, you know, bespoke headers that I've invented, X-geo -city, postal code, region, region code, and appending the text of what city, postal code, region and region code the user is from.

And so this is. Sorry, go ahead, John. No, no, it's great. I just I remember the first time, like the things that you can accomplish with this, like even though it's such a small amount of code or like just so many, like I have like like static websites that I have where I just like read out of the Cloudflare request object and I like read that, you know, like the time zone or I read like the city that you're in or like I've seen really cool like applications like I think Jay Phelps has like his startup and on the homepage of his startup, it says like, you know, San Francisco is a long way from and it's just your city, but it's like a static page.

So I remember the first time I saw that, I was like, you know, how are people accomplishing this?

And then started looking into like it's like a year or two ago, like looking into Cloudflare Workers and looking into like how you get this really cool dynamic data that you can just append to static requests because it's just all in the Cloudflare object.

Yeah, I don't actually don't want to start on Jay's stuff.

I think that's really cool because it brings the usability of client side A-B testing to the server side, performance penalties.

But I think that's for another for another talk. But but but for this one, like what you have had to do in the past is, OK, I get a request coming in.

I'm going to now use a third party address provider and lookup provider. I'm making a synchronous request over to them to get that information and storing it maybe as a cached version of my database.

All that stuff kind of just goes away. All the geolocation code that's in my application is gone.

And what I can do instead is just read stuff from headers.

So in my application code, I just say, what's the value of Xgeocity?

Right. And so at the Cloudflare level, all I'm doing is appending the information and saying, let the application figure out what to do with it.

And the application just says, what is the information? And I know what to do with it rather than having to mix those two concerns together in your application.

Yeah, it's really clean. And yeah, it's interesting because you do have like a few different options, like, you know, like one option you could have to kind of push things more onto the worker level.

So you could use something like I've seen apps that use Cloudflare's HTML rewriter before.

Right. So they'll like grab the content that it's about to send down and then they'll check like the other city, postal code, something like that.

And then they'll append or like they'll augment the DOM in some way.

But I do like this idea. Like you have this front end app that's already doing this logic.

You basically have been able to cut out this like whatever, like third party API that you're using or a bunch of application code that's doing a lot of stuff.

And like I really like this header approach. It's quite simple.

Like I think it makes this code super easy to read and test. And then your application code just keeps doing what it was doing in the first place.

And kind of a win win there.

That's exactly right. Yeah. And yeah, there's just a plug for the excellent docs for for workers is tons of examples that you can just copy and paste that include HTML rewriter, which has a nice, clean API that lets you do all types of really neat stuff.

Yeah. Yeah, it's pretty. Yeah, I was just going to say that the last little bit of this thing is once you're finished creating your new request, all you're ever doing is returning a promise of the fetch that's coming out of out of your request.

And so actually, I believe this is a promise in itself.

So this probably doesn't even need to say anything. But anyway, you're returning the fetch.

And then that goes back to over here, comes out of a promise to respond with and respond with will respond to requests with a new promise that is the fetch of the of the new request.

And that signals to Cloudflare workers that this is what gets forwarded over to your origin server as the result.

Yeah, it's really great.

I saw I saw something that kind of blew my mind the other day with like so our our panel request functions, they like return, you know, a fetch or fetch request.

Right. And so I'd like seen this other day where people were using it to hit external fetch requests, but not having to do that whole dance of like calling that JSON and no async await or anything like that, because the Cloudflare function itself handles that request event.

Right. That handles a response.

And so, yeah, it's a kind of this really nice, clean API. Like I've been having a lot of fun kind of playing with it.

It kind of is it feels pretty nice and easy to work with here.

Yeah. And what's really nice is the request and response objects are like a pretty standard.

And yes, you see a lot of the industry right now, Remix as well, in particular, who are like embracing using the vanilla request and response objects and just see a nice symmetry across the industry for adoption of what are ultimately web standards in non like this is not a web context, but it's essentially the same thing.

I know James has been doing a lot of work there to bring the standards over there.

Absolutely. Yeah. James on our team has been doing just awesome work with like getting you know, we've had a lot of things where we've like offered APIs that look and for a lot of intents and purposes perform similarly to the web spec, but aren't fully spec compliance.

He's been doing a bunch of amazing work on there. And then it's funny that you mentioned Remix because it was a Remix app that I saw doing this.

Like so this Remix app was like displaying things from the GitHub API.

But since they use the same standards compliant fetch that we use, they were just simply able to be like return fetch, you know, API dot GitHub dot, you know, just just super clean endpoints that we're just able to do that kind of stuff.

And they play really nicely together.

Yeah, I'm a big fan of them. They actually work. I know that's like the thing.

Like I was. Yeah, I feel like we've gotten to this kind of point where we've had like a lot of bumps between framework authors kind of doing their own thing or maybe holes in the browser ecosystem.

But I do feel like we're heading towards like some pretty cool adoption or like private companies, open source libraries and the browsers are all kind of like coming around some of these standards.

And like the fetch one is a really nice one to to support.

Yeah, no, it's that's been a big unlock, I think, for our industry, for sure.

Awesome. OK, so example one, really clean check to see if it's got the Cloudflare object and then append all the geolocation data that you want to the headers and then do no other business logic in the worker.

Just send that request back. I love it. So the other one is very similar.

Same also has a lot of this is also just, again, another example of setting headers.

So a lot of the same familiar code just kind of wanted to show off Cloudflare's bot management software.

And so if you don't know, Cloudflare also can do machine learning based scoring of incoming requests.

And so a big problem, I wouldn't say actually a big problem, but a problem that every big site has, Skillshare included, is you have to just deal with a lot of bad actors.

And so, you know, common tactics of like rate limiting or IP address blocking or this or that, even CAPTCHA these days doesn't work.

So there's a lot of bad actors who they will get a CAPTCHA, they'll go send it out to a content farm or a CAPTCHA answering service.

They'll pay a penny to get the CAPTCHA answered, then use the response and then try to like, you know, input a credit card to steal credit cards.

Right. So all of the like low tech, I would say, including CAPTCHA solutions that you have for fighting malicious actors are kind of, you know, not working anymore.

And so what I really like about Cloudflare bot management, it's machine learning based, it's adaptive.

And basically every incoming request for bot management gets a score and gets a notion about whether it's a verified bot.

Right. And so the score is essentially a score between zero and one, with one indicating that it's not a bot and zero indicating that it is a bot.

And then a verified bot means it's something like Google, you know, Google bot or some other like known non-malicious actor like Yandex or one of these things that will spider your site regularly.

And so very similar here.

All we're doing is seeing if bot management is in the request object. And if it is, then we are appending the bot score as a header and we're appending whether it's a verified bot to the header.

And so, again, I'm a big fan of separation of concerns and putting the work here.

But you could, if you wanted to, very quickly augment this to serve another page, like an error page or just anything you really want there.

Right. You can just return, you know, if the score is over a certain amount and if the path is like your login page or your sign up page, send it to like a 405 or 404 or whatever thing you think is the right answer for the end user.

Yeah.

Yeah, no, I love this. I remember this is like another thing that is just fascinating to me because I like I had done some stuff, like I had implemented captures at jobs before and things like that, like Google recapture or whatever.

And then I remember watching a conference talk, it's like probably five years ago from the folks at Shape Security.

And they started talking about all these things that I just didn't wasn't really aware of, you know, like credit card stuffing and like malicious web scraping and like, you know, I'm like trying to or like or like review spam on different websites.

If you offer, you know, I was like starting to think about all these different ways that these like automated things can like break or like really diminish your website's experience and how like kind of like you mentioned, like not only do tools like captures, like really not not work.

Right. I mean, they've been cracked, but they also like don't prevent this like very wide range of malicious user.

Like this could be not a user that's trying to submit a form.

They're like they're like purely just trying to like scrape your website really quickly over and over again.

Or they could be like, I don't know.

I remember seeing like I think another one that is an example of is like the people like putting items in your shopping cart to get them off the inventory and then and then not buying them.

So like I remember that was like an attack vector.

So I think that this like abstraction where it's not about. I always really like where Cloudflare draws their abstractions.

I think that's what I'm trying to say is that instead of trying to come up with like individual products for each of the like six main types of attacks or whatever, like we abstracted away and we give you this kind of simple score and you still have a lot of flexibility.

Right.

Like you don't have to not. So it's not like we say we won't serve the request if the score is too high.

It's still up to you. But I like the level of that. It's abstracted where it's like this looks fishy or this doesn't look fishy as opposed to like implementing at the actual attack vector.

Yeah, no, it's just funny because like you could just turn on DDoS protection if you wanted to and like get get the benefits of that.

But then you also have this sophisticated thing. So I think what I really like about Cloudflare is that it's there's almost always a push button version that gets the ninety nine percent use case.

And there's the non push button version that you have the flexibility, but always simple APIs, always the ability to kind of just use what you need to then go put the power in your in your end application as well.

So I really love it. Yeah, you know, it's when I was talking to Jared from from shape and he's like the person I always think of every time I every time I look at this code.

Yeah, me too. I'm like, OK, yes, Jared would be happy to know I'm doing something a little bit more than than a capture capture.

Right. Yeah, I should have him on sometime to talk about security stuff.

That'd be awesome. Yeah, it's really cool. And I love like your point earlier.

Like, I just feel like there's all this stuff that needs doing, you know, and it's like, oh, I want to be safe.

I want to protect. I want to like, you know, whatever.

And then like the abstraction level is so important to me. Right.

As like a busy person, like just the idea that like I can go into the cloud for dashboard and click activate on any button or even a hundred buttons or something like that.

But, you know, if you offer it in like an SDK form or like some code that you have to add and get code review, you know, all this stuff, it can be a real challenge.

So like I think that I think it's just a it's a really nice experience just to be able to go in and be like, oh, that sounds smart.

Click it on and then do something like this where you just kind of pass the headers through.

And I got to tell you, like not to not to fanboy on on Cloudflare too hard, but like as an organization, being at the forefront of pushing like the actual infrastructure of the web forward.

Right. So I felt like I got to participate in like a web through HTTP three, like becoming a real thing because there was a button.

I'm like waiting for this button to like be able to be turned on, waiting for Chrome to get support for HTTP three.

I'm like that day I could turn that on and say like I support HTTP three.

Yeah, that's like a significant move forward. Like the day I could turn on AVIF compression rather than just, you know, WebP, you know, it's like we're moving forward.

We're like shrinking bandwidth. We're moving forward performance for users.

And again, I get to say we because I'm a customer. Yeah, but I really was another big one for me.

I remember I'm so excited to have brought everything just got smaller for free, like no downside.

Right. Like it was just like the button, everything is faster.

Yeah. I mean, the compression use case is just for me the most the most obvious.

Right. Because coming from a front end world like you see these tutorials and not for nothing, they're the right thing where you're configuring WebP to like produce broadly assets that you then bundle them.

And then you get a whole bunch of things you need to do.

Right. Which is like fine for static website.

And it's fine for the average end user. But with Cloudflare, that's literally OK.

That's a button. Right. That's a button I push or a five line function I write.

You know, you're talking about like even more use cases of you could theoretically in here do on the fly bundling of assets if you want.

People absolutely.

Right. Yeah. Yeah. So I was really interested to hear, like, because I think that I.

I spent a lot of time like hanging out with two different groups of folks, I think there's like folks that are like very worker savvy, serverless savvy, whatever, and they're just looking for like value adds, like what features we offer first competitors or whatever.

And then I think there's like another group of folks who are like, yeah, I've heard of these technologies before, like not even just to talk about Cloudflare in general.

But I was wondering if you could like just at a high level, walk me through like how you think about serverless.

Like we kind of talked about the beginning.

I just found it fascinating, like architecture, like when you would consider adding kind of a new layer or like kind of how you think about that stuff as you're building product engineering, basically.

Yeah. Sorry if that's a heavy question.

No, no, no, no. It got me really intrigued when you were mentioning it earlier.

Well, the question really is like so I think about separation of concerns and I think about like systems architecture from a people perspective first, actually.

Right. And so if it's maybe not, maybe it's Conway's law, I forget which one it is, which just says the organizational structure and communication structure you have will be reflective of the or the technology structure to stack Right.

Reflective of the organizational structure you have. Right.

So we really are all in on loosely coupled organization and you do want to produce things that can move independently.

Then serverless is like, OK, I could deploy one function.

Right. I could deploy this line of code only. And Brian LaRue talks about this all the time, like he deploys for five seconds because he's deploying one function.

Right. And so when we're talking about a 50 person organization like Skillshare and we're deploying the production 20 times a day, which is like where we're at, like having those have to go in sequence because they're all part of a large monolith is not sustainable.

Right. And so you pull pieces off into microservices.

And what's the end goal of microservices? You're talking about functions.

Right. Yeah. And so I think just like it's a company maturity thing.

I think it's a size of organization thing. I think it's mostly around being able to clearly define boundaries as well.

And so. Yeah. Sorry, I'm going long on this question, but like Martin.

No, no, no. I really love it. I think it's just, yeah, it's giving me good, good things to think about.

I really like that answer.

Yeah. So I think. Go ahead. Oh, please. I was just going to say, like, I think for me, it was interesting.

Like my experience at Adobe is we sort of went from like extremely monolithic services into like dipping our toe into the serverless world.

Like I think there was like the three phases were like we had the kind of standard like 2000 era, like pre-serverless architecture of like, you know, this is the core service.

There's like more like an onion, right? You'd have like your core service, then you'd have like your layer one, layer two, whatever.

And and all these pain long deploys or like long testing periods, really hard to get work prioritized, really hard for new developers to ramp up in these big, you know, all these things.

And then we started first just allowing still still not serverless, still like full service services, but letting engineers outside of that or make their own services on top.

That was like a big step for us.

Right. Like so you could write like a little Golang service that interacted with the core services.

Now you're still deploying a full like monolith.

Yeah. Through all the stuff. But then but then for me, the big thing was like one, like when we started messing with some serverless stuff, like one, you know, the ease, the speed, scaling, all that stuff.

You don't have to worry about it so much.

You're not deploying the production service. You're just deploying your function.

But two, which was so exciting, is like a lot more people can contribute like out of nowhere, like out of nowhere.

It was like every team would just have their most like back end person, you know, just volunteer.

And like, you know, they would just be like, oh, I can figure that out.

Like I can go write a node or like some go or like, you know, whatever.

And like that was like really big for us, like to be able to like, you know, have front end folks or mid-level folks that were just not not comfortable jumping into like whatever, like Java or like C++ monolith or whatever, but just to write a little bit of TypeScript and, you know, send it up and boom, have this thing.

Like I feel like that opens these like floodgates to like just really cool possibilities.

Oh, I mean, 120 percent for sure.

It's not having to care about the the the back end or the infrastructure as much is a huge unlock for a lot of people.

Yeah. Yeah, I think it's really cool. And and I also just really like, you know, if you're working at Skillshare or like Adobe or whatever and you work on a monolith, like a core monolith service, like you have to be protective of it.

Right. Like that's everything. That's like that's a story in the news.

It's lost revenue if it goes down. Like you really have to be. But when folks are like building a new, you know, like a new web app or a new desktop app and they build their own new APIs, like, you know, there's really only so much harm that you can do in that world.

Right. Where you're just like, I think it's OK to like loosen some of those restrictions and like let front end engineers build their own service.

Like, you know, and it's just really exciting for me.

I think it opens up like a lot of like new doors that that's what I try to get people pumped about.

Yeah. No, I mean, I think workers is a great use case for that for sure.

Gateway technology, as they say. Yeah. Yeah. Well, awesome. I think we've got a couple of minutes left.

I was just kind of like I guess I just kind of wanted to like poke poke around a little bit more like if you had advice for folks who are who are in that group who are like starting to dip their toe in or like maybe their front end folks or mobile folks that haven't really done services before.

Like, do you have any kind of recommendations for like kind of getting started?

I love these header services.

That seems like a beautiful way to get started is like the next time you need something, build a serverless function that adds the headers that you need.

I think that's like awesome. Yeah, I think that's a great idea.

I think in general, like identify a small use case, identify something that will one is look at the examples on the Cloudflare doc site like there.

That will that will prime the pump and figure out which one is is useful for you.

There's like, you know, I don't know, 50, 60 on there.

There's quite a few. One of them is going to be really useful for you.

And then it's not going to take it's not going to take you long to have it up and running.

It won't. So just give it a shot, you know, 10 to 20 lines of code.

Most of it's copy and pasteable and then tunable to your needs.

And, you know, just go for it. Yeah. Yeah. And I think to like, again, not to be too much of a I'm not here for like a sales mission or anything like that, but I also think the current price point is very reasonable to test something out.

Like I've been working with a few companies like midsize companies that are unsure.

They're not Cloudflare customers and they're like, well, we have a lot of traffic.

And so they put a worker in front of something and they're getting like their, you know, five or seven thousand hits a day, which is a tremendous amount of traffic.

And then looking at the price point, I think it's like a pretty affordable thing that you can try out, you know, cover the bill for, see if it helps and not be too far in.

Yeah, I agree. Awesome. Anything else before we wrap up here?

I really appreciate you coming on. It's like very fun looking at those examples and geeking out about this stuff.

Yeah, no, I appreciate you having me on. Really enjoyed talking about Cloudflare and just FYI, Skillshare is hiring.

Yeah. Go check out Skillshare.

You can get a job or learn how to paint or any number of awesome things.

So thanks again so much, John. I really appreciate it. Yeah, thanks so much.

I really appreciate it. Thank you for coming on.

Thumbnail image for video "Full Stack Week"

Full Stack Week
Relive the exciting announcements from Full Stack Week! Check out the Full Stack Week Hub to find all of our announcements and blog posts!
Watch more episodes