Contract first vs code first: history is repeating itself

With the increased popularity of Graphql, so does the popularity of the frameworks such as (for python) graphene and Ariadne.

They are two frameworks able to reach the same target: a working GraphQL backend service catering for Graphql queries, mutation, and subscriptions.

However, they are taking opposite routes.

Graphene is taking code first route. So developers would write the python to tell the services the information it can server (resolve or mutate) and how to serve them. Something like:

import graphene# Define your domain object, you can couple it with Django if they are from DBclass Company(DjangoObjectType):
class Meta:
model = dm.Company
....
def resolve_staffs(self, info, **kwargs):
return self.staffs.all()
## Define your resolversclass CompanyQuery(graphene.ObjectType):
companies= graphene.List(graphene.NonNull(Company), required=True)
company = graphene.Field(Company)

def resolve_company(self, info, **kwargs):
....
return dm.User.objects.get(user_id=user_id)

def resolve_companies(self, info, access_group_id=None):
model = dm.Company.objects

....
return model.filter(....)
## include into the Query
class Query(
CompanyQuery,
....
):
pass
## include different types
schema = graphene.Schema(query=Query, ....)

It starts with the code, to define the domain object and then the service how to resolve that instance, then will generate the contract people can reach:

For Ariadne, it will start the other way around, so we will first define the schemas:

## define the domain object
type CompanyInfo {
name: String!
address: [String!]!
staff: [Satff!]!
owner: String!
....
}
## define the resolver
type Query {
companies: [CompanyInfo!]!
....
}
## Then we load the schema
schema = load_schema_from_path("...graphql")
## Then link the schema with the resolver
make_executable_schema(
schema, company_resolver()....
)
## define the resolver
company = ObjectType("CompanyInfo")

@company.field("address")
def resolve_address(
_: None, info: GraphQLResolveInfo, ....
) -> FrameworkInstance:
return get_address(....)

Both routes will reach the same destination:

Now come with the debate: some prefer the Ariadne approach, claiming it’s for better decoupling.

However, looking back at history, this is the same debates happened many times:

We have this debate with web services, between contract first vs contract last, WSDL to code vs Code to WSDL.

We have this debate with ORM, whether domain first or last.

But what has happened? Both approaches survived through time.

Actually similar to DI, a lot of developers are aware of the decoupling writing the components through separate configuration files (XML). However, tight coupling wins out, we are almost using annotation everywhere.

The claim of schema first or contract first, so that it could be decoupled and have a single source of truth, actually worked out with code first as well. Look at what happens at REST services. We start with code first, write the services, then leave out to swagger to expose the single source of truth (the contract).

Ultimately, I think it boils to the maturity of the tooling. If either side has supreme tooling over the other side, it will win out regardless of concepts.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store