Full-stack software engineer building useful tools. Constantly learning, with a habit of connecting dots across different domains.
NOTES 14
OPEN →- 01
- 02
- 03
- 04
- 05
General Lessons from Building Stylify
These are our reflections and lessons from building Stylify. What we tried, what failed, what worked, and what we would do differently. Looking back, better decisions, earlier testing, and clearer boundaries could have saved us considerable time and effort. We believe the central lesson applies not only to Stylify, but to similar AI-powered products: they succeed when deterministic software controls the boundaries, protects user data, preserves important state, and verifies every operation, while the model is used only where genuine interpretation is required.
What is Stylify?
Stylify is an AI-assisted Microsoft Word tool that helps users turn inconsistent or poorly structured documents into clear, professionally formatted documents. A user can describe the desired result in plain language, apply a house style from a template, or work on a specific selection. Stylify interprets that request, identifies the document’s existing structure, proposes the necessary changes for review, and applies only the changes the user approves.
The product has several responsibilities that are easy to confuse:
- discover the structure already present in a document
- apply or repair semantic structure such as headings, lists, and tables
- apply visual styling such as fonts, spacing, colors, borders, and page setup
- perform narrowly authorized text cleanup
- preserve content and formatting that the user did not ask to change
Stylify is therefore not simply an AI that rewrites a document. Treating all these responsibilities as one generation task can create something impressive in a demonstration but unsafe in a real document. Stylify separates structure, styling, and text cleanup into different paths. Formatting mode promises not to change the user’s words, while text-changing operations must be explicitly requested, separately authorized, and independently verified.
Its styling system follows the same principle of explicit control. General defaults form the base, followed by the document’s detected style, a selected template, instructions inferred from the prompt, or explicit overrides determine the final decision in that order e.g a template can establish a house style, but it should not erase a direct instruction.
What Building Stylify Taught Us About Reliable AI Products
The following are lessons we learned the hard way and will carry with us when building our next product. We believe they apply broadly to AI products that interpret user intent, work with valuable data, and take actions on a user’s behalf.
Use AI for interpretation, not control
Models are useful when a request is ambiguous, contextual, or difficult to express as fixed rules. They should not decide security boundaries, mutation scope, permissions, identity, or whether an operation succeeded. Those responsibilities belong to deterministic software that can be inspected, tested, and reproduced.
In Stylify, the model might interpret an instruction such as “make this section look more professional” and propose an appropriate style. Deterministic code still decides which paragraphs belong to the selection, which properties may change, and whether the operation was successfully applied. The model can suggest what should happen, but it cannot silently expand the change to nearby content.
Separate understanding from execution
Interpreting what a user wants and changing their data are different responsibilities. A model may propose an action, but deterministic code should always validate, constrain, and execute it. This separation prevents a plausible model response from becoming an unsafe write.
In Stylify, the model can interpret “turn these notes into a table” and propose the resulting structure. Before Word is changed, deterministic code checks that the proposed table maps to the selected source paragraphs, preserves their content, and does not absorb unrelated text.
Make scope explicit and fail closed
The system must know exactly what the user authorized it to change. If a selection, target, permission, or source identity cannot be resolved, the operation should stop. Uncertainty must never silently widen a small request into a larger one.
In Stylify, if Word cannot reliably report the boundaries of a selected passage, the operation is stopped and the user is asked to select the text again. The system does not fall back to formatting the whole paragraph or document.
Preserve what the user did not ask to change
A reliable product must protect everything outside the user’s request. Existing data, structure, metadata, relationships, and user intent should remain untouched unless changing them is necessary and explicitly authorized.
In Stylify, we made sure that changing the font or spacing of a heading would not rebuild the paragraph, remove its inline formatting, alter its wording, or affect surrounding content. The implementation changes only the requested Word properties and leaves the original document objects in place whenever possible.
Prefer narrow mutations over reconstruction
Changing only the necessary property is usually safer than rebuilding an object from an incomplete representation. Reconstruction can discard information the system never captured, so replacement should be reserved for genuine structural changes and limited to the smallest verifiable area.
In Stylify, we made sure that a request to change formatting updated the existing Word objects directly. Rebuilding content was reserved for structural requests, such as converting prose into a table, and even then the replacement was limited to the exact source paragraphs involved.
Keep identity and provenance throughout the pipeline
Every proposed change should remain connected to the original data that authorized it. Positions and generated structures may change during processing, but source identity must survive interpretation, review, approval, and execution. Without that connection, the system cannot reliably determine what may be changed, preserved, or rejected.
In Stylify, we made sure that every generated block remained linked to the original Word paragraphs from which it came. If paragraphs were split, merged, reordered, or converted into a table, those source references travelled with them. This allowed the review and execution stages to apply approved changes to the correct content without relying on fragile output positions or repeated text searches.
Treat review as part of execution
A review screen is trustworthy only when it represents the exact operation that will be applied. The preview, approval state, and execution payload should all come from the same model of the proposed change. Limitations, skipped operations, and unsupported behavior should be visible before the user approves anything.
In Stylify, we made sure that the Result view, comparison views, Keep/Revert decisions, and final Word operation were derived from the same approved change set.
Verify effects, not just successful calls
A successful function call does not prove that the intended change occurred. A reliable product should distinguish between operations that were applied, skipped, unsupported, ineffective, refused, or failed. Where practical, it should verify the resulting state instead of treating the absence of an error as success.
In Stylify, we learned that Word could accept an API call without producing the expected visible result. We therefore tracked how many changes were actually applied and reported operations that had no effect or could not be supported by the current Word host. This prevented the interface from claiming success simply because execution completed without throwing an exception.
Test boundaries in the real environment early
Mocks and unit tests are essential, but they cannot prove host-specific behavior, object lifetimes, permissions, rendering, caching, or platform support. The riskiest integration boundaries should be tested early in every supported environment rather than left until the end.
In Stylify, code that worked in Word for the web failed in Word for Mac because the two hosts handled Office.js paragraph objects differently across synchronization calls. Earlier testing on both hosts would have exposed that difference before the surrounding implementation was treated as complete.
Build observability before difficult debugging begins
Errors should contain enough context to identify the failing layer, operation, environment, and state. Logs should preserve useful error codes and locations without exposing secrets. Good observability replaces repeated guesswork with evidence and shortens the path from a reported symptom to its actual cause.
In Stylify, the message about being unable to determine a selection’s boundaries initially suggested several possible causes. Once the Word task-pane logs were forwarded to the desktop app, the failure became precise: Word for Mac returned ItemNotFound at ParagraphCollection.getItem. Capturing that detail earlier would have saved time spent investigating the backend, selection text, and other unrelated parts of the system.
Turn every important bug into an invariant
An important bug should produce more than a local patch. It should become a durable rule, test, validation step, or architectural constraint that prevents the same class of failure elsewhere. Otherwise, the immediate symptom disappears while the underlying mistake remains free to return in another form.
In Stylify, a formatting request once widened beyond the intended target when a selector could not be resolved. The lasting lesson was not merely to repair that selector, but to establish a system-wide invariant: an unresolved target must never fall back to the whole document. That rule could then be enforced across selection handling, planning, review, and execution.
Prefer evidence over assumptions
Platform behavior, model performance, compatibility, and safety should be established through focused experiments and captured failures rather than confident assumptions. When a real failure exists, replaying that exact case is usually more valuable than creating a cleaner example that may omit the condition that mattered.
In Stylify, we repeatedly returned to the last failing log, replayed the exact model response without calling the model again, and traced it through validation, review, and execution. This separated reproducible software defects from model randomness and helped us test proposed fixes against the failure that had actually occurred.
Design for partial failure
AI products combine models, networks, native applications, external APIs, and local state, so some component will eventually be unavailable or behave differently than expected. Optional capabilities should degrade gracefully, while uncertainty involving permissions, identity, scope, or destructive changes should stop the operation safely.
In Stylify, an unavailable model-enrichment stage could fall back to deterministic document structure and produce a less polished but still safe result. By contrast, if the selected range could not be verified or a structural replacement could not preserve protected content, the operation was refused rather than partially applied.
Introduce complexity only when evidence requires it
Large abstractions, broad schemas, additional model stages, and flexible fallback behavior can create more failure modes than they solve. Start with the narrowest reliable contract, understand its limitations, and expand only when real evidence shows that the additional complexity is necessary.