Modeling a big spaghetti app
The problem
At work, we've been modularizing our old Rails monolith for the better part of 2 years. We've made a ton of progress and I've personally learned a lot. Unfortunately, it's been challenging to share details publicly because all the examples are closed source, and sensitive. If I wanted to write any open-source any visualization tooling, it'd be hard to test on anything resembling a real project.
I recently made some progress in this problem space. LLMs are great at synthetic data generation, and it turns out that Claude 3.5 Sonnet can, given the right combination of prompts, produce a pretty mean looking spaghetti ball monolith.
First, I asked Claude to generate me a ball of mud model layer.
I didn't feel like this was "monolith-y enough" so I coaxed it to get more tangled. I also asked for it to provide edge descriptions and a few different user types.
That's what I'm talking about.
Then, in a new session, I asked if Claude could generate a graphql schema for this app. It did a good job on it's first attempt. I asked it in a subsequent request to add documentation to the schema. I also needed to tell it to use relay... I ended up with this:
# Root Query type
# Provides entry points for reading data from the system
type Query {
# Retrieves a user by their unique identifier
user(id: ID!): User
# Fetches a product by its unique identifier
product(id: ID!): Product
# Retrieves an order by its unique identifier
order(id: ID!): Order
# Fetches a category by its unique identifier
category(id: ID!): Category
# Retrieves a support ticket by its unique identifier
supportTicket(id: ID!): SupportTicket
# Fetches a warehouse by its unique identifier
warehouse(id: ID!): Warehouse
# Retrieves a paginated list of products with optional filtering
products(first: Int, after: String, filter: ProductFilterInput): ProductConnection!
# Fetches a paginated list of orders with optional filtering
orders(first: Int, after: String, filter: OrderFilterInput): OrderConnection!
}
# Root Mutation type
# Provides entry points for modifying data in the system
type Mutation {
# Creates a new order
createOrder(input: CreateOrderInput!): Order
# Updates an existing product's information
updateProduct(id: ID!, input: UpdateProductInput!): Product
# Creates a new support ticket
createSupportTicket(input: CreateSupportTicketInput!): SupportTicket
# Updates the inventory quantity for a product
updateInventory(id: ID!, quantity: Int!): Inventory
}
# Represents a user in the system
type User {
id: ID!
email: String!
profile: Profile
# Paginated list of user's addresses
addresses(first: Int, after: String): AddressConnection!
# Paginated list of user's orders
orders(first: Int, after: String): OrderConnection!
# Paginated list of user's reviews
reviews(first: Int, after: String): ReviewConnection!
wishlist: Wishlist
shoppingCart: ShoppingCart
# Paginated list of user's notifications
notifications(first: Int, after: String): NotificationConnection!
# Paginated list of user's payment methods
paymentMethods(first: Int, after: String): PaymentMethodConnection!
# Paginated list of user's membership plans
membershipPlans(first: Int, after: String): MembershipPlanConnection!
role: Role
loyaltyPoints: Int
}
# Represents a product in the system
type Product {
id: ID!
name: String!
description: String
price: Float!
category: Category
inventory: Inventory
# Paginated list of product reviews
reviews(first: Int, after: String): ReviewConnection!
# Paginated list of product images
images(first: Int, after: String): ImageConnection!
# Paginated list of product attributes
attributes(first: Int, after: String): AttributeConnection!
# Paginated list of related products
relatedProducts(first: Int, after: String): ProductConnection!
supplier: Supplier
}
# Represents an order in the system
type Order {
id: ID!
user: User!
# Paginated list of order items
orderItems(first: Int, after: String): OrderItemConnection!
totalAmount: Float!
status: OrderStatus!
shippingAddress: Address!
billingAddress: Address!
# Paginated list of payment transactions for this order
paymentTransactions(first: Int, after: String): PaymentTransactionConnection!
# Paginated list of shipments for this order
shipments(first: Int, after: String): ShipmentConnection!
createdAt: DateTime!
}
# Represents a product category
type Category {
id: ID!
name: String!
parentCategory: Category
# Paginated list of subcategories
subcategories(first: Int, after: String): CategoryConnection!
# Paginated list of products in this category
products(first: Int, after: String): ProductConnection!
}
# Represents the inventory of a product in a specific warehouse
type Inventory {
id: ID!
product: Product!
quantity: Int!
warehouse: Warehouse!
# Paginated list of stock movements for this inventory
stockMovements(first: Int, after: String): StockMovementConnection!
}
# Represents a warehouse in the system
type Warehouse {
id: ID!
name: String!
address: Address!
# Paginated list of inventories in this warehouse
inventories(first: Int, after: String): InventoryConnection!
manager: User
}
# Represents a customer support ticket
type SupportTicket {
id: ID!
customer: User!
subject: String!
description: String!
status: TicketStatus!
agent: User
# Paginated list of responses to this ticket
responses(first: Int, after: String): TicketResponseConnection!
createdAt: DateTime!
updatedAt: DateTime!
}
# Represents a product supplier
type Supplier {
id: ID!
name: String!
# Paginated list of products supplied by this supplier
products(first: Int, after: String): ProductConnection!
qualityRating: Float
}
# Represents a product review
type Review {
id: ID!
product: Product!
user: User!
rating: Int!
comment: String
createdAt: DateTime!
}
# Represents a payment transaction
type PaymentTransaction {
id: ID!
order: Order!
amount: Float!
status: PaymentStatus!
paymentMethod: PaymentMethod!
createdAt: DateTime!
}
# Represents a shipment for an order
type Shipment {
id: ID!
order: Order!
trackingNumber: String
status: ShipmentStatus!
estimatedDelivery: DateTime
}
# Represents a notification sent to a user
type Notification {
id: ID!
user: User!
message: String!
type: NotificationType!
read: Boolean!
createdAt: DateTime!
}
# Represents a user's profile information
type Profile {
firstName: String!
lastName: String!
phoneNumber: String
dateOfBirth: DateTime
}
# Represents an address
type Address {
id: ID!
street: String!
city: String!
state: String!
country: String!
zipCode: String!
}
# Represents a user's wishlist
type Wishlist {
id: ID!
user: User!
# Paginated list of products in the wishlist
products(first: Int, after: String): ProductConnection!
}
# Represents a user's shopping cart
type ShoppingCart {
id: ID!
user: User!
# Paginated list of items in the cart
items(first: Int, after: String): CartItemConnection!
totalAmount: Float!
}
# Represents a payment method
type PaymentMethod {
id: ID!
type: PaymentMethodType!
lastFourDigits: String
expirationDate: DateTime
}
# Represents a user's membership plan
type MembershipPlan {
id: ID!
user: User!
plan: MembershipPlanType!
startDate: DateTime!
endDate: DateTime
}
# Represents a user role with associated permissions
type Role {
id: ID!
name: String!
permissions: [Permission!]!
}
# Represents a single permission
type Permission {
id: ID!
name: String!
description: String
}
# Represents an image, typically associated with a product
type Image {
id: ID!
url: String!
altText: String
}
# Represents a product attribute
type Attribute {
id: ID!
name: String!
value: String!
}
# Represents an item in an order
type OrderItem {
id: ID!
product: Product!
quantity: Int!
price: Float!
}
# Represents a stock movement (inbound, outbound, or adjustment)
type StockMovement {
id: ID!
inventory: Inventory!
quantity: Int!
type: StockMovementType!
createdAt: DateTime!
}
# Represents a response to a support ticket
type TicketResponse {
id: ID!
ticket: SupportTicket!
responder: User!
message: String!
createdAt: DateTime!
}
# Represents an item in a shopping cart
type CartItem {
id: ID!
product: Product!
quantity: Int!
}
# Represents a type of membership plan
type MembershipPlanType {
id: ID!
name: String!
price: Float!
duration: Int!
features: [String!]!
}
# Connection Types
# These types enable pagination for lists of objects
type AddressConnection {
edges: [AddressEdge!]!
pageInfo: PageInfo!
}
type AddressEdge {
node: Address!
cursor: String!
}
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
}
type OrderEdge {
node: Order!
cursor: String!
}
type ReviewConnection {
edges: [ReviewEdge!]!
pageInfo: PageInfo!
}
type ReviewEdge {
node: Review!
cursor: String!
}
type NotificationConnection {
edges: [NotificationEdge!]!
pageInfo: PageInfo!
}
type NotificationEdge {
node: Notification!
cursor: String!
}
type PaymentMethodConnection {
edges: [PaymentMethodEdge!]!
pageInfo: PageInfo!
}
type PaymentMethodEdge {
node: PaymentMethod!
cursor: String!
}
type MembershipPlanConnection {
edges: [MembershipPlanEdge!]!
pageInfo: PageInfo!
}
type MembershipPlanEdge {
node: MembershipPlan!
cursor: String!
}
type ProductConnection {
edges: [ProductEdge!]!
pageInfo: PageInfo!
}
type ProductEdge {
node: Product!
cursor: String!
}
type ImageConnection {
edges: [ImageEdge!]!
pageInfo: PageInfo!
}
type ImageEdge {
node: Image!
cursor: String!
}
type AttributeConnection {
edges: [AttributeEdge!]!
pageInfo: PageInfo!
}
type AttributeEdge {
node: Attribute!
cursor: String!
}
type OrderItemConnection {
edges: [OrderItemEdge!]!
pageInfo: PageInfo!
}
type OrderItemEdge {
node: OrderItem!
cursor: String!
}
type PaymentTransactionConnection {
edges: [PaymentTransactionEdge!]!
pageInfo: PageInfo!
}
type PaymentTransactionEdge {
node: PaymentTransaction!
cursor: String!
}
type ShipmentConnection {
edges: [ShipmentEdge!]!
pageInfo: PageInfo!
}
type ShipmentEdge {
node: Shipment!
cursor: String!
}
type CategoryConnection {
edges: [CategoryEdge!]!
pageInfo: PageInfo!
}
type CategoryEdge {
node: Category!
cursor: String!
}
type StockMovementConnection {
edges: [StockMovementEdge!]!
pageInfo: PageInfo!
}
type StockMovementEdge {
node: StockMovement!
cursor: String!
}
type InventoryConnection {
edges: [InventoryEdge!]!
pageInfo: PageInfo!
}
type InventoryEdge {
node: Inventory!
cursor: String!
}
type TicketResponseConnection {
edges: [TicketResponseEdge!]!
pageInfo: PageInfo!
}
type TicketResponseEdge {
node: TicketResponse!
cursor: String!
}
type CartItemConnection {
edges: [CartItemEdge!]!
pageInfo: PageInfo!
}
type CartItemEdge {
node: CartItem!
cursor: String!
}
# Represents pagination information for connections
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# Enums
# Represents the possible statuses of an order
enum OrderStatus {
PENDING
PROCESSING
SHIPPED
DELIVERED
CANCELLED
}
# Represents the possible statuses of a support ticket
enum TicketStatus {
OPEN
IN_PROGRESS
RESOLVED
CLOSED
}
# Represents the possible statuses of a payment transaction
enum PaymentStatus {
PENDING
COMPLETED
FAILED
REFUNDED
}
# Represents the possible statuses of a shipment
enum ShipmentStatus {
PROCESSING
SHIPPED
IN_TRANSIT
DELIVERED
}
# Represents the types of notifications that can be sent to users
enum NotificationType {
ORDER_UPDATE
PRICE_DROP
BACK_IN_STOCK
PROMOTIONAL
}
# Represents the types of payment methods available
enum PaymentMethodType {
CREDIT_CARD
DEBIT_CARD
PAYPAL
BANK_TRANSFER
}
# Represents the types of stock movements
enum StockMovementType {
INBOUND
OUTBOUND
ADJUSTMENT
}
# Input types for mutations
# Input for creating a new order
input CreateOrderInput {
userId: ID!
orderItems: [OrderItemInput!]!
shippingAddressId: ID!
billingAddressId: ID!
paymentMethodId: ID!
}
# Input for specifying order items when creating an order
input OrderItemInput {
productId: ID!
quantity: Int!
}
# Input for updating a product's information
input UpdateProductInput {
name: String
description: String
price: Float
categoryId: ID
}
# Input for creating a new support ticket
input CreateSupportTicketInput {
customerId: ID!
subject: String!
description: String!
}
# Input for filtering products in queries
input ProductFilterInput {
categoryId: ID
minPrice: Float
maxPrice: Float
inStock: Boolean
}
# Input for filtering orders in queries
input OrderFilterInput {
status: OrderStatus
minTotalAmount: Float
maxTotalAmount: Float
fromDate: DateTime
toDate: DateTime
}
# Custom scalar for handling dates and times
scalar DateTime
First of all, this schema works. If you want to play with it, you can. You can go to this glitch server I hooked up that will actually run. If you send GraphQL requests to this endpoint, you'll get responses... and if you navigate there in your browser, you can play with the schema.
![apollo server](/img/model-spaghetti/graphql-server.png)
Next, I wanted to see if it could generate rails code in a D2 diagram. It took some coaxing, but I asked it to generate D2 nodes with ActiveRecord source code in them. Here's the model graph.
OK, what about the GraphQL layer?
Next, I asked Claude to imagine what a service layer might look like... I had a hard time getting Claude to do "bad" software engineering, so the service layer actually looks pretty reasonable to me.
Want to see what the whole monstrosity looks like?
Yep... that's a big ball of mud right there. That's a plate of Spaghetti. That's something that needs refactoring.
So why is this so cool? Beyond the fact that Claude's abilities are neat, it demonstrates that with an LLM we can make realistic, runnable synthetic code. Next, I'm going to try to use this code as a sort of lab for studying legacy enterprise codebases. Perhaps it will make it easier for us to share techniques across organizations and compare notes.
- Previous: Get shit done by warping friction-space