ERIC SAXBY: Thanks for coming.
So, I am Eric Saxby. Today I'm gonna be talking
about iterating towards service-oriented
architecture. I may have time for questions
at the end, but I may not.
This might go forty minutes,
so I'm just gonna get started.
So, if you're like me, you can't really pay
attention in talking about programming
unless there are pictures
of cats. So really, we're going from this to
something a little bit more like this. But working
together, as a family.
So why should you care? Why should you listen
to me? So you may not know this, but
I'm kind of a big deal. But, but really,
I've, I've actually not been doing this for that
long. I've been doing Rails for about six years.
Before and after that, I had been using various
different technologies. And I have been fortunate to work
with some very smart people and be able to
learn from them and to break a lot of
things really quickly. And right now I work at
Wanelo. Also, I'm trying to collect all of the
buzzwords on my resume. I have more. I have
more than just this.
But, so, why is Wanelo important? We're like a
social network for shopping, but really it's because we
have many millions of users, active users, and databases
with billions of records. And we've gone through the
pain of getting there and keeping the site actually
running. So, you can save any product from any
store on the internet into your own collections and
have your own wishlist. That's what we do.
More importantly, we've gone from having this as one
main Rails application, doing all of this, to a
central Rails application that's still fairly large, but supported
by a lot of services. We've done as much
as we could open source. Some of the, the
business domain logic, it's really hard to open source.
But, but we try as much as possible.
We've done almost all of this in Ruby, including
some things that people who prefer other languages say
can't be done in Ruby. And we've done this
with a very, very small team very quickly.
If you're like me, though, you're really not so
interested in the success story as you're interested in,
how did you screw up, how did you, how
did you break. So, let me take you on
a journey to another company that a friend of
mine once, recently, called "The Anti-Pattern Goldmine." Completely hypothetical
company. Not naming any names. That I may or
may not have worked for. Some of you in
the audience may or may not have worked for.
After some of this story, you might think you
did. Come in, it's a startup. It's new. Small
team. And come in and say, wow. For a
startup, you have a lot of code that's really,
really tangled. It's all Rails 2 code base. You
know, if you remember vendoring Rails, we, we did
that. If you remember how vendoring Rails can go
wrong? Yeah, yeah. Yeah, that was there. That was
there.
And I think a lot of this might have
come down to the fact that at least early
on, success of a product, success of a feature,
really was launching it as quickly as possible. And
no, no, no, no. Don't worry about that stuff.
Don't worry about design. And we have thirty engineers
doing that as rapidly as possible. Like, five or
six teams all doing that as rapidly as possible,
trying to get it into production. And releases were
a mess, you know. I'm sure a lot of
you can relate to this. Deployments. Multiple hours with
all of this engineer, all these engineers trying to
put all of this code in as quickly as
possible. Invariably, deployments went wrong.
And eventually this got a little bit faster from,
from monthly releases to weekly releases. But then we
would have code freezes, where for three months, everyone's
trying to cram in all the features without deploying,
and you can imagine where that goes.
And, over time, really, things are just getting worse.
We, our rough estimates that we're trying to give
to see how long things are gonna last come
back, and we say, oh, that means we were
deploying on this date. You know, hey, we told
the board, great. That's awesome. You're gonna deploy on
this date. Ah. Whoo. With a deadline like that,
the only really way to meet it is to
make serious shortcuts. And then as soon as a
product finishes, because we're invariably missing those deadlines, and
there's the next project that is supposed to be
out in, in a week, based on the estimates
that you were supposed to do. Team gets dispersed.
Finish these new projects. And no matter how worse
case we are in our estimates, it's just never
worst case enough. It just, it just keeps getting
worse.
So some of you might, might be familiar with
this story. So, during the course of this, I,
I, I, I think I've learned a lot. You
know, programming is one of the most fun careers
that I've, that I've had, and when it's not
fun, you know, you know something's wrong. And keep
reading about service-oriented architecture, and, you know, a lot
of us latch onto this. This is the solution
to all of our problems. And you know at
dev ops, that's pretty cool over there. You know,
dev ops is the answer. That's how we're gonna
do services. It's just gonna, it's a done deal.
All we have to do is do services, dev
opsy, and like, we're done.
So around this time, I move into the operations,
not, not the operations team, cause dev ops. Not
the operations team. Not the team over there that
you just throw the things over the wall and
they just make it work. No, no, no. Not,
not the operations team at all.
And certainly me, a number of other people in
the engineering team, I, I could say, really decided
services are the only way forward. And really quickly,
product, and, and I don't mean an individual project
manager or anything like that. By product, I mean
really, all of the people, all of the teams
going into designing the product, planning the product, really
how it was going forward, they quickly come to
the conclusion that services are really just that, just
that, that thing getting in the way of us
cranking out these features. Cause features is success.
But at first, if at first you can't succeed,
I have, I have, I have learned, you know,
there's, there's a few things. You can take a
firm stance, breathe deeply, and become the loudest person
in the room. Really, really helps. Also, if there's
anything that I've learned from my cats, it's throw
shit, you know. Done. It really, really helps in
situations like this.
So, we, we have a few things coming up.
A few products. These, these aren't necessarily concurrent, these,
these features. These projects. But we have a new
login feature and login, in this application, is as
homegrown as you could possibly imagine it could go
wrong. And it's this core, core login functionality, and
we're like, no, no, no, no, no, no. No,
no, no, no, no, no. That's not going in
this code base. That's gonna be a new application,
we're. That's the only way we're gonna do this.
Otherwise we're just not gonna do this. It's gonna
fail. We know it's gonna fail. So let's just
skip to not doing it.
And, also, we have this enterprise software over here,
and we have this homegrown Rails application, and all
the data really needs to kind of be synced
between it in order for the company to actually
work. So a lot of iterations on this, but
really this time, we're gonna do it right, and
it's gonna be a, a data service. We're gonna
have our Enterprise software in our Rails app and
that is totally just gonna make this an Enterprise-Rails
app. It's gonna be amazing.
And I remember saying this a lot to a
lot of different people. Like, we're gonna screw this
up. We're gonna fail. I know we're gonna fail.
But it's the only option that we have. It's
the only way that we're actually gonna be able
to get to be able to do this and
succeed at some point in the future.
OK, so on, on a side track, hopefully this
is not new to very many of you, but
really there's a lot of different ways to do
service-oriented architecture. It can mean a lot of different
things. Read blog posts. You get a lot of
different ideas. It can be synchronous, asynchronous. Use a
message bus. Maybe some services require actually hitting them
over HTTP, TCP sockets. You know, there's a lot
of ways of different doing, of doing this kind
of thing.
But why would you really want to? So, you
know, scale the organization. You have a lot of
different teams. A lot of different engineers. Maybe you
really want this team over here to just have
their own code base that they deploy separately. Also,
maybe you want to outsource this to a different
team, and you really don't want to give all
of the code to the other team. You really
just want to isolate this and say, you know,
you guys over here, you know, just, here, here's
this. Sorry. I'm, I'm actually trying to explain it
all gendered pronounce from my vocabulary but it's really
hard. Sorry.
So, you can also scale the code base, you
know. Performance of this thing over here might be
completely different than this thing over here, and that
might actually be easier in a service. You can
really tune the code to the work load. You
might have this really complicated code that, for various
reasons, needs to be complicated. It might be really
complicated functionality. But you can, you might be able
to hide that behind a, a clean interface. An
API.
And tests, usually, not always, but a small code
base can mean fast tests. And if you're sitting
around for hours waiting for tests to complete, you
know, that can be, that can really eat into
your productivity. But it, but it comes at a
cost. All of this comes at a cost.
And I think one of the things that, that
I've been learning is that sometimes the cost of
not doing this is greater than the cost of
doing. The cost of the infrastructure, new servers, how
they talk together. It's really complicated. Things will go
wrong. But sometimes not doing this is gonna, is
gonna mean that your productivity is going down and
down and down and, and it actually is more
costly.
OK, so back to these projects. We have this
data service. It's sometimes, I think six engineers, sometimes
eight. I don't, I don't really remember. And nine
months of, of work. It's really complicated, like state
transactions, and this is really critical data that really
needs to be done right. And there are, so,
there, there might have been, when deploying it, some,
some slight data things. But they were fixable. Really
quickly. It was fine. There were actually no major
data problems.
And there were some applications that were built on
top of this data service. And depending on who
you talk to, they, they were more or less
successful. Some people using that, these applications are like,
oh, thank you, this did exactly what, what I
need. It's actually helping me with my workflow. Some
people are like, oh, OK.
But engineering, you know what, this was really critical
data. It was really hard. Totally new for us.
This was a success. We did not break anything.
Product is like nine months? Nine months. Eight engineers,
nine months. How could you possibly call that a
success?
OK. So different application. Login flow. Depending on the
time period, two engineers, four engineers, plus the dev
ops team. Dev ops. Three months, you know. You
know, we were figuring out, like, the system's automation
was really tangled and in, in a few weeks
we had some staging servers. And about two months
later, someone comes up to me and says, hey,
where, where, where are the servers gonna, gonna be?
Can we get those? And I'm like, deep breath.
Deep breath. Which servers? Oh, this, those staging servers.
But it went, we worked it all out.
Something that I learned about dev ops. Everyone actually
needs to be invested and it's new to people.
So, and it was released with a feature flag.
Only a small amount of people were actually running
through it. So it, we had some really good
time to break it in production and really figure
out how, how it was really gonna run.
And we launched it to all of our users,
and it was actually, I would say, very successful.
It worked exactly intended. It was very resilient to
failures. We had to do some unexpected database maintenance.
Restart master databases and we're like, you know what,
just do it. You know, just, just restart the
database. No, no, no, no, no. Don't turn off
the service. It's gonna recover. It's gonna be fine.
Maybe like one user will notice.
And the company's actually sort of figuring out that
user metrics might be more important than dates as
metrics, and the user metrics were generally successful. So
engineering, like, this, this is good. This is good.
This is, this is how we do this. This
is, you know, the next project, this is how
we're gonna, we're gonna have to figure this.
And products is like three months? What are you
talking about? Success? That's not success. We have these
other product features that needed to be done. And
we have these four engineers that are sitting in
the corner, not able to do these things.
OK. So I would say that this is a
really important question to ask. What is, what is
success? If engineering says it's a success, and product
says it's a failure, who really wins? So, this
is actually a trick question, because it's a zero-sum
game. Like, nobody wins in this interaction.
So let's go a little bit deeper to ask
why we needed SOA in the first place. And,
I, this is really complicated. There's lots of moving
parts, but some of the things that, that I
think now were because engineering didn't trust product, and
we actually didn't, what that really means is we
didn't trust that we could do our jobs well
given the shortcuts that we needed to do to
actually do our job and actually meet our metrics.
And product didn't trust engineering to meet the products
we were given, and we, we couldn't actually meet
those promises. And, again, over time, this was changing,
but product was accountable for features, not or quality,
and, and the actual how users interacted with them.
And this is probably the subject of much more
discussion, and would love to continue this and, and
learn more myself, but I think that a lot
of this comes down to trust. If, if, if
you can't trust that you can do your job
successfully, how, how can you actually do your job
successfully? If, if product, if different parts of the
company don't trust each other to do their jobs
well, how can they actually do their jobs well?
So, what did, what did we learn? You know,
fuck it, next time it's SOA from the beginning.
Like, before we even do Rails new it's gonna
be RabbitMQ. We're gonna have a cluster. It's gonna
be amazing. So, no. No, no, no. Not, not,
not the right answer. I almost went here, and
thankfully I worked, then, with some very humble, empathetic
people, who were also very convincing. No.
So, I think what I've, what I have really
taken from this, is Agile's not just one sprint
after the next. It's like four miles is great.
You just break it up into a hundred meter
increments and you just, each one is a sprint.
It's gonna be fine.
What this really is about iterating. Deploying things, small
things, really quickly, using the data that you get
from that to figure out what to do next,
and refactoring. Refactoring is really, really important. You might
be able to do a small thing quickly, and
with some shortcuts, not really needing to know how
it needs to be designed. But when you see
the pattern, you, you have to fix it. And
SOA's not gonna solve larger organizational problems.
It's not gonna fix your code. Really what it
is is another tool at our disposal to solve
our pain points, and our, and our organization's, our
company's pain points. And how we do that is
through iteration. So, small changes deployed, deployed quickly, using
feature flags so that you can actually get code
into production as soon as possible, knowing that it's
off and it's not gonna affect users. And prioritizing
this when it's necessary.
So when might it be necessary? I would say
performance. That's a really big thing. This is actually
driving a lot of what we're doing. Code complexity
might be less in a service than it would
be outside of a service. So if you have
these two things and you're like, this is actually
gonna be easier to do over here than to
put in this tangled mess, maybe, maybe it is
better.
May, and also maybe, sometimes you have a new
feature and it is completely unrelated to everything else
that you've already done. With the caveat that you,
in the short term, you might be able to
put it in with all the rest of that
mess, trusting that when it becomes a problem you
will be empowered to fix that problem.
OK. So, performance. As I said, this is driving
a lot of, of our work. So, Winelow is
getting slower. We're running into some problems. We just
got ring that the databases are becoming I/O bound
on disk. This is a really, really bad place
to be. But our user growth is increasing dramatically.
The, the activity is increasing dramatically. We're starting to
see exponential tendencies in our graphs, and if you
see exponential tendencies in your user graphs, it's kind
of like giving a talk to hundreds of people.
You're like, why are you all looking at me?!
It's, it can be scary. It can be really
scary. But we have, so we have one table
that's really outstripping the others, and that's causing problems.
That's, it's, we discover, because of our data, because
of our graphs, that it's really one table that's
really destroying the performance of the rest of the
table. And we're in the cloud, cause we have
all those buzzwords. So there's really a maximum size
of a host that we can get. So there's
really, like, an upper limit of how much we
can actually solve this with one database.
Even after read-write splitting, we've already done that, and
we, and, and pretty soon, we realized that the
site is gonna break. If we don't do anything,
we're just not gonna have a company anymore. It's
just gonna, it's gonna fall down. But, we have
really, really, really active committed users joining us right
now. Now is not the time to stop feature-development.
Now's the time to really learn from what our
users are doing, double down on it, really tweak
our features and figure out what is gonna make
our business successful.
And we only have ten engineers at this point.
We don't really have that much, that many resources
to, to, to work with this.
So, our first step of iteration is realizing that
this is one problem. How do we solve this
one problem? And this is maybe going to be
a service. How do we get to a point
where it can be a service? So first step
is isolating the data. So ActiveRecord gives you these
really nice things. Associations, has_many, has_one. Things that really
make the joins between your tables easier.
When you have a service, you don't have joins.
So these need to go away. These, these just
don't exist. But it's actually really easy to get
rid of these, honestly. A product has saves, you
know. You save this product into a collection. You
know, we could just take that where clause that,
that ActiveRecord was gonna sort of do for us
with, with a join, and just pull it out
as a, as a where.
Really not that hard, actually. And you know what,
ActiveRecord also gives us ways of talking to a
different database. But, you know, we can actually use
this to just pretend it's in a different database.
Establish connection allows you to have this model live
in this database and all the rest of your
stuff live in your main frames. It's really not
that hard.
And, one of the key things is that each
step of this, each slight change, is deployable, and
it's testable. And you can deploy it to staging.
You can click around to see where, where your
test coverage is maybe missing. Figure out where, where
it breaks. And one thing is that, that I
will say, is you might be doubling your database
connections at this point. And when you database hits
the max connections, just everything stops. It just stops
working.
So, we've learned this lesson the hard way. But
now that we have this code deployed that pretends
like it's in a different database, we can make
it a different database. Without actually that much work.
You have a Postgres - we love Postgres -
we have a master database. And we spin up
a replica over here, and we put the site
into maintenance mode. If you have more critical interactions
with your company that maintenance mode might, might not
be possible, brain tree has some really great blogs
and, I think, talks about this. But, for us,
the operational overhead of making it so we can
do this without the site being down was way
more than it was worth.
So we just take the site down. Push in
new database dot yml, saying now this connection is
talking to this database. And we just promote that
to master and restart everything, bring the site back,
and now we have two databases. Five minutes of
downtime. Not, not that bad, actually. And you know,
after the fact, you can clean up. You know,
we have a redundant table over here. A bunch
of redundant tables over here. You just truncate them
and delete them. Very carefully.
Not that hard. And actually, you know what, at
this point, you might not need a service. You
might be done. Your site might just be working.
It's fine. For us, we, we knew, based on
how things were going, that we were gonna have
to horizontally shared this data. Now it's in a
different database. That is gonna have to be many
databases.
And we want to hide that complexity. We don't,
we do not want our main application to have
any of that code. So, we know we're gonna
have a service. Now isolate the interface. And now
what this, by this, I mean, how are you
actually accessing this data? And what is your long-term
goal? You know, ours is sharding, and Wanelo is
saves, and anytime we're actually getting saves, we're either
looking at by product - a product has these
saves - or we have it by user. A
user has saved these things.
So, this is actually really helpful, how to plan
out what, your DSL, what your API is gonna
look like, you know. We know that things are
gonna have to get to it via product or
via user. And so, you have some where clauses,
you know. Where is ActiveRecord? We are not gonna
have ActiveRecord.
So, instead a save has a by_product method that
is also. Oh, so one thing I will say
is, at this point it's really helpful to remove
redundancy. If you have different ways of accessing kind
of the same data, do you really need that?
You know. Can you change the product to mean
that you don't actually have as many of these
fingers as you have. And very soon, things are
gonna break. So if you don't have tests, this
is a really good place to add tests.
So, now, we have a, a small sort of
Ruby DSL. How do we actually pull that out?
And I would say, right now, it actually doesn't
need to be a service. Really what you need
is a client. And how do you build the
client out? And that's where adapters really come in.
So, we use the module. This could be a
base class. Some of the reasons why we thought
we needed a module. Maybe we didn't Maybe we
could have actually done this as, as a base
class. But now, a save is a SaveClient. And
that SaveClient is where your fingers go as, their
class methods. And one thing I would point out
is that that, that finder is calling through to
an adapter. A database adapter.
And really, that's what's wrapping up all your database
calls. Hiding them from, from your application. And really
one of the core pieces of this is that
your database, your, your adapter is your feature flag.
It's also deployable in place, you know. You, you
can have this in your lib folder. You can
actually start a gem now. And you can just
deploy it and it's, your main application is still
talking directly to this other database. But you're starting
to build out your client. And later, when you
have a service, you can replace it with a
different adapter.
So that adapter gives you back, when you, when
you call, like, a, you know, you get save.by_product
and you call all on it, that's actually, when
you call by_product, you're getting back a relation. And
this is something that we thought we didn't need,
but turns out ActiveRecord does this for very good
reason, and that's because threads and because state.
If you say I want this type of data
and you save it away to a variable, and
now you call some other method, order.by_this, and it
changes state, you might do something else on this
variable over here, not realizing that you've altered its
state later. So any time you make a change,
a state change on these objects, you really want
to get back a new instance. A different instance
with different state. And when you call, you know,
all, first, or pluck any of these things, what
you're really calling it on is your relation instance.
And the, the key thing that we learned is
that that relation is sharable between all of your
adapters. So the actual work done to get the
data is on your adapters. So the relation delegates
back.
So, in our database adapter, the thing actually getting
the data, is ActiveRecord. We've just moved our ActiveRecord
class into the library and hidden it from our
application. We, in this case, we were using ActiveRecord,
so you could just, we'd do ActiveRecord. If you
have another favorite database adapter, great.
So you call Save.by_product. You, you get an adapter
that, or so the, sorry, that calls through to
the adapter and gives you back relation. You call
Relation dot all and that just delegates back to
the adapter, which calls through to an ActiveRecord, gets
to your data, takes the attributes out of it
and gives you back an instance of, of your
core class. Because you're hiding ActiveRecord. You don't want
to get back an ActiveRecord class.
And I would say it's critical to deploy at
this steps, because you've made mistakes. I guarantee you've
made mistakes. And the cost of fixing this is
really low right now, as opposed to spending a
lot of time trying to design your server, how
that's going to interact, and realize, whoa, whoa, whoa,
whoa, we screwed up the client. All of this
work we've done on the service, we have to
through away because we did it wrong.
Now, you have a client. Now you need a
server. And it doesn't matter. What, whatever you want.
It's fine. It's actually the, the cost of writing
this is really, really low. Because the server is
the simplest part of this, and if you did
it wrong, if you chose the wrong transport method,
mechanism, you know what? You build a new adapter.
That's actually really quite easy.
So let me take a moment to just reiterate,
why should we have deployed by now? And I'd
say it's because the client is much, much more
complicated than the server, so your bugs are in
the client. Your bugs are not in the server
at this point. And the server is going to
be dictated by the choices you've made in the
client. So if you've made wrong choices and you
build your server, you've built the wrong server.
We used Sinatra and Oj, just cause it's awesome.
It just, it really just works. It's small and
it's useful. We thought we would need to move
away from HTTP, but we've grown a lot and
we haven't had to do this yet. It just
works. Things that we thought we would have to
change immediately, you know, it's almost a year later
and it's just.
OK, so now we use the service. And that's
really a feature flag. You just write a new
adapter that talks to the service instead of the
database. So now you call, you know, by_product, you
get a HTTP, it calls through to the HTTPAdapter
which gets you back the same time of relation.
When you call all on it, you know, it
calls adapter dot all, which now goes to an
HTTPClass that actually gets the JSON and takes the
attributes out of it and gives you back your
save class. You're getting back the same object you're
getting back as save.
So, retrospective. Great. We've isolated the data. We've isolated
the interface. We started to build our DSL. We've
pulled that DSL out into a gem. Now we've,
now that we actually kind of understand what that
gem needs to do, we can launch the service
and then just build a new adapter to switch
to this.
If, I would say that if we hadn't, if
we had realized that this was the order that
we needed to do it on, we, we would
have done this in two weeks. Instead. So that
first part of it was like a day worth
of work. That second part of it was like,
three hours worth of work. And deployed immediately.
The, the harder part was realizing that we needed
an adapter. And at this point, people, we didn't
really see anything about hexagonal architecture. This might have
been before some of those talks and papers have
been coming out. But, but it's actually really useful.
Tests. We, we use Sunspot for some of our
Solar things. And we're already used to spinning up
a Solar instance using a gem, trap-it-up. You know
you can do that for your integration tests. But
for unit tests, you know what, we have tests
around all of this. So we can have tests
around a fake adapter that proves that it behaves
the right way, and then that just saves, saves
data in, in memory, in your application.
And redundant tests, you know, you might say, eh,
do I really need this test? Yes. Because one
thing, you can delete your tests later that are
redundant, and you want to be really confident that,
that when you do switch over, it's gonna work.
Foreman and Subcontractor are, are really helpful for this
kind of thing. So subcontractor is a gem that
really says, for this service, change directory over here
and run this command in a completely isolated Bundler
environment. Cause you really don't want to mix your,
your dependencies. You don't want your server dependencies and
the versions to be conflicting with your main application
dependencies. You want to be able to, to change
those separately.
OK. So what about a new app? I'm spinning
up a completely new thing. Not extracting. Totally new.
How do I do that? How do I iterate
on something that doesn't exist yet? And I would
say that some of the lessons that, that we've
learned from this, and actually from just doing our
product development in general is iterate. Find a way
to get to the smallest deployable thing as quickly
as possible, and whatever tool you use to deploy,
to spin up infrastructure, one of the sort of
heuristics that, that, that we've found is really focus
on organizing that code around being able to change
things easily, and understand how this thing is different
from this thing.
You know, the Chef makes it really easy to
define a giant hash of global state over here
and then just run this thing over here and
it's magic and it'll just do it. When you
actually start to spin up different services, this thing
is gonna need to be slightly different than this
thing. So how can your code make that as
easily understandable as possible?
So feature flags, also, on or off. Do customers
see this, or they don't. But you know what?
Maybe it's just kind of half on. Maybe everything,
every request that's gonna actually call through this, use
Sidekiq to just every time, just spin off a
job to hammer your, your service. And if it
breaks, you've learned that before you've launched it to,
to your users. There's a lot of other ways
you can do this.
On to these five years users, and let's see
how we can break it, see how the interaction
feels. It's, it's really useful.
Also, one thing that we found is, it's often
helpful to inter, integrate very deeply before you go
widely. So, if you have a code-interaction that goes
through all this process, do it kind of once
through all the process, without worrying so much about
the edge cases or the errors, because those are,
you're gonna find those. Those are gonna, those are
gonna come up. But it's re- really useful to
kind of go all the way through the interaction
knowing that it's really kind of sketch and doesn't
do everything, and really let that drive the design.
And this is also a thing of like letting
the feature kind of drive the design and figuring
out, what is the pattern for how you really
need to organize the code, before you have. Don't
just like whiteboard everything and say this is gonna
be perfect. You know.
Production is going to destroy every single design that
you think you have. Also, if something seems clever,
it's bad. If you're like, eh, this is kind
of cool. No. No, no, no. It's bad. Complexity
is gonna come to you. Don't, don't seek it
out. It's, it's, it's evil.
So if there are any kind of take aways
from this, I would say you know, hexagonal architecture
is really cool, but you don't have to design
it from the start. If you have trust, if
everyone in the organization has trust that you can
do your job and that you're all going, working
together to build an awesome product, an awesome company,
you can fix this later. You can actually let
the needs of your product determine where your boundaries
are.
So, thank you. I actually have a few minutes
of, of questions, to answer questions.