# Ali Demir — complete writing > Essays and notes by Ali Demir. Text-only, structured for both human and machine readers. Author: Ali Demir. Generated: 2026-07-27. Contains 5 posts, newest first. Canonical site: https://alideemir.com --- ## My First Blog Post URL: https://alideemir.com/blog/my-blog-post/ Published: 2026-07-27 Tags: data, ai, systems _This post is the first publish of mine which explains why I started this blog._ ## Who Am I? A psychology graduate which started his career as social media manager and moved into data analytics. I am specialized on digital data which includes raw data of Adjust, GA4, Appsflyer, marketing channel owned data etc. Hope to see you again. #### Why I Started This Blog You might wonder how someone goes from studying human behavior in a psychology lab to staring at event schemas, attribution models, and SQL queries all day. To me, the transition felt completely natural. **Psychology is the study of human behavior; digital data analytics is the measurement of that behavior at scale.** Every click, session, app install, and custom event tracked by tools like **GA4**, **Adjust**, or **AppsFlyer** is ultimately a digital footprint of human decision-making. My time as a social media manager gave me a front-row seat to how audiences interact with content on the surface. Moving into data analytics allowed me to look under the hood—uncovering the *why* behind the numbers through raw, unvarnished data. I created this space to: - **Document My Learnings:** The best way to master complex data concepts is to explain them simply. - **Bridge the Gap:** Translate raw technical marketing data into clear, actionable business and behavioral insights. - **Build a Practical Resource:** Share real-world challenges and solutions regarding MarTech, attribution, and data pipelines. #### What You Can Expect Here Moving forward, I will be writing about the practical and technical sides of digital analytics, including: - **MMP & Web Analytics Deep Dives:** Working with raw event streams from **Adjust**, **AppsFlyer**, and **GA4**. - **Data Wrangling & Pipelines:** SQL queries, Python scripts, and logic to clean and aggregate marketing channel data. - **Behavioral Marketing Analytics:** Connecting user psychology to retention, churn, and conversion funnel optimizations. #### Let’s Connect Whether you are a fellow analyst, a growth marketer trying to make sense of your ad spend attribution, or someone interested in the intersection of psychology and tech, I hope you find something valuable here. Feel free to reach out, drop a comment, or share your thoughts on future posts. **Thanks for reading, and stay tuned for the next deep dive!** --- ## On deleting the image URL: https://alideemir.com/blog/on-deleting-the-image/ Published: 2026-07-24 Tags: writing _A note on what happened to my writing when I removed the option of showing instead of telling._ I removed images from this site expecting to miss them. What I did not expect was how quickly it exposed which paragraphs had been coasting. A diagram is a promise that the idea has a shape. Very often it does not, and the diagram is doing the work of hiding that — boxes and arrows arranged confidently enough that neither the writer nor the reader has to notice the idea underneath was never articulated. Take the diagram away and the sentence has to be written. Sometimes the sentence turns out to be easy, and the diagram was a courtesy. More often the sentence turns out to be hard, and that difficulty was the actual subject. The same is true of screenshots. A screenshot of an error is a way of not writing the error down. It looks like evidence and functions as an excuse. What I have found is that the constraint is not really about machines, though that is how I arrived at it. It is about the fact that anything worth saying can be said, and that reaching for a picture is most tempting at exactly the moment when I have not yet worked out what I mean. I do lose things. Some ideas are genuinely spatial and I describe them worse than I could have drawn them. I have decided that is an acceptable price for closing an escape hatch I was using dishonestly. --- ## What I check before shipping a site URL: https://alideemir.com/blog/what-i-check-before-shipping/ Published: 2026-07-22 Tags: process, the web _A working checklist, refined over several launches, for the things that are cheap to verify now and expensive to discover later._ Every item here exists because I once skipped it and paid for it. The list is ordered by how much it costs to find the problem after launch rather than before. ### Content reaches the reader - View source on the deployed page, not the local one. If the article text is not in the raw HTML, no crawler that matters will see it. - Disable JavaScript entirely and reload. Whatever remains is what most machine readers get. - Fetch a page with `curl` and read the output. Browsers repair broken markup silently; `curl` does not flatter you. - Confirm the canonical URL is absolute, correct, and points at the version you actually want indexed. - Check that a page with no content — an empty category, a tag with one post — still renders something coherent rather than an empty shell. ### Structure holds up - Read the heading outline on its own. It should be a fair summary of the page. If it is not, the headings are decoration. - Verify there are no skipped heading levels. An `h2` followed by an `h4` is a claim about structure that is not true. - Confirm exactly one `h1` per page, and that it says what the page is rather than what the site is. - Check that every link's text makes sense read in isolation. "Read more" appears in an index of links with no context around it. - Make sure the language attribute on `html` is set and correct. It is one attribute and it changes how the page is pronounced, hyphenated, and translated. ### The machine-facing files exist and are right - Load `/robots.txt` and read it aloud. Misplaced `Disallow` lines are the single most expensive typo available on the web. - Load the sitemap and count the URLs. If the number is wrong, something is being excluded silently. - Fetch the RSS feed and put it through a validator. Feeds fail quietly and nobody reports it. - Check that drafts are absent from all three. It is easy to exclude a draft from the page listing and forget the feed. ### Typography survives contact with reality - Read a long post on a phone, outdoors, at whatever brightness you actually use. Contrast decisions made on a calibrated monitor in a dim room do not survive this. - Resize the window to the narrowest and widest plausible sizes and watch the measure. Lines longer than about seventy-five characters stop being readable, and nothing warns you. - Print a page, or at least open the print preview. Print stylesheets are almost always broken, because almost nobody looks. - Check the page in both colour schemes, including the moment of switching. A theme that only works in one direction is a theme that was tested once. - Look at a paragraph of text with the fonts blocked. If the layout collapses without the webfont, it will collapse for the first paint too. ### Nothing is quietly broken - Click every link in the navigation and footer. This takes two minutes and catches the embarrassing ones. - Load the site with a cold cache on a throttled connection and time it honestly. - Check the 404 page exists, is styled, and offers a way back. - Confirm the favicon actually resolves. A missing one produces a 404 on every single page load. - Search for your own domain in the output of a full crawl for stray `localhost` or staging URLs. --- The list is not exhaustive and it is not meant to be. It is the set of checks whose cost is under a minute each and whose failure is measured in weeks of quiet damage. Anything that stops meeting that ratio comes off the list, and anything that starts meeting it goes on. --- ## Serving Markdown alongside your HTML URL: https://alideemir.com/blog/serving-markdown-alongside-html/ Published: 2026-07-19 Tags: astro, the web, code _A short, practical guide to giving every page a plain-text twin, and why that beats hoping a crawler parses your markup correctly._ If you want a machine to understand your writing, the most effective thing you can do is stop making it read HTML. Hand it the source instead. The pattern is simple: for every page at `/blog/some-post/`, also serve the original Markdown at `/blog/some-post.md`. No navigation, no header, no footer, no class attributes — just the text as written. This post covers how to do it in Astro, and the three details that are easy to get wrong. ### The route Astro decides a route from the filename minus its final extension, so a file named `[...slug].md.ts` produces routes ending in `.md`. It sits happily beside the `.astro` route that renders the same content as a page. ```ts // src/pages/blog/[...slug].md.ts import type { APIRoute, GetStaticPaths } from 'astro'; import { getCollection } from 'astro:content'; export const getStaticPaths: GetStaticPaths = async () => { const posts = await getCollection('posts'); return posts.map((post) => ({ params: { slug: post.id }, props: { post }, })); }; export const GET: APIRoute = ({ props }) => { const { post } = props; return new Response(post.body, { headers: { 'Content-Type': 'text/markdown; charset=utf-8' }, }); }; ``` That is the whole mechanism. `post.body` is the raw file content with the frontmatter already stripped, which is exactly what you want to hand over. ### Detail one: put the metadata back Stripping frontmatter is right for rendering and wrong for this. A consumer fetching the `.md` file has no page around it to tell them who wrote the thing or when. Put a small block back on the way out: ```ts const frontmatter = [ '---', `title: ${JSON.stringify(data.title)}`, `published: ${data.publishedAt.toISOString().slice(0, 10)}`, `canonical: https://example.com/blog/${post.id}/`, '---', ].join('\n'); return new Response(`${frontmatter}\n\n# ${data.title}\n\n${post.body}`); ``` Use `JSON.stringify` for any string that reaches YAML. Titles contain colons and quotation marks more often than you would guess, and a title like `Astro: a review` will produce invalid YAML if you interpolate it bare. The `canonical` line matters more than it looks. It is what stops the Markdown twin from being treated as a competing copy of the page rather than a representation of it. ### Detail two: advertise it A file nobody knows about is not an interface. Link it from the HTML page so it can be discovered rather than guessed at: ```html ``` I also link it visibly in the post footer. There is no reason to hide it from people — the plain-text version is genuinely nicer to read on a bad connection, and the kind of reader who notices it is the kind of reader worth having. ### Detail three: the content type is not automatic In a static build the `.md` files land on disk, and from that point the `Content-Type` header is your host's decision, not Astro's. Many hosts serve unknown extensions as `application/octet-stream`, which makes browsers download the file instead of displaying it. On Netlify: ```toml # netlify.toml [[headers]] for = "/*.md" [headers.values] Content-Type = "text/markdown; charset=utf-8" ``` On Vercel: ```json { "headers": [ { "source": "/(.*).md", "headers": [ { "key": "Content-Type", "value": "text/markdown; charset=utf-8" } ] } ] } ``` Check it after your first deploy. `curl -sI https://example.com/blog/some-post.md | grep -i content-type` takes five seconds and will tell you immediately whether the whole exercise is working. ### Then do the same thing for the site Once every post has a text twin, an index over them costs almost nothing, and the `llms.txt` convention gives you a shape to put it in: a title, a one-line summary, and a list of links that each carry a description. Point the links at the `.md` files rather than the pages. ``` # Your Site > One sentence about what this site is. ## Writing - [Post title](https://example.com/blog/post.md): What it argues, in one line. ``` A model arriving at that file can see the entire site at once and fetch only what it needs, instead of crawling the navigation and inferring the rest. The whole thing is about forty lines of code and it replaces a great deal of guessing. --- ## Writing for machines that cannot see URL: https://alideemir.com/blog/writing-for-machines-that-cannot-see/ Published: 2026-07-14 Tags: writing, the web _Most of the web is now read first by something that will never look at it. That changes what a page owes its reader._ There is a particular kind of vanity in a website that looks magnificent and says nothing. For most of the last decade this was a survivable flaw, because the audience was a person with eyes, and a person with eyes can be persuaded by a good photograph and a confident headline. That audience has not disappeared. But it has been joined by another one, and the second audience cannot see anything at all. When a language model reads a page, it does not experience the page. It receives a sequence of characters. Whatever meaning survives that transformation is the only meaning the page has. The hero image is gone. The careful spacing is gone. The colour that took an afternoon to choose is gone. What remains is the text, and the shape the text was given by its markup, and nothing else. If the argument of a page was carried by anything other than those two things, the argument does not arrive. This is not a complaint. I think it is one of the more clarifying constraints to appear in web design in years, because it is a constraint that rewards exactly the thing that was always supposed to matter. A page that reads well as plain text was, almost always, a page that was written well in the first place. The discipline of writing for something that cannot see is the discipline of having something to say. ### The failure is quiet What makes this difficult is that the failure mode gives no warning. A site that renders its content with client-side JavaScript looks perfect to its author, perfect to its designer, perfect in every screenshot, and arrives at a crawler as an empty shell with a loading spinner in it. There is no error. Nothing is red. The page simply contains nothing, and no one finds out, because the people checking are people, and people run JavaScript. The measurements bear this out more starkly than I expected. When Vercel instrumented real crawler traffic against their own infrastructure, they found that none of the major AI crawlers rendered JavaScript at all. Anthropic's crawler downloaded JavaScript files in roughly a quarter of its requests and never executed a single one. OpenAI's did much the same. The only significant exception was Google's, which inherits a rendering pipeline built over fifteen years for an entirely different purpose. So a page can hold the first result on Google and be, to every other reader that matters now, a blank sheet. I find the asymmetry worth sitting with. We spent a decade being told that the rendering gap had closed, that Googlebot could handle a single-page application, that server-side rendering was an optimisation rather than a requirement. All of that was true, and all of it quietly stopped being true, and almost nobody was told. ### What the text has to carry Once you accept that the text is the whole artefact, some decisions that felt like matters of taste turn into matters of function. Headings stop being typographic furniture and become the table of contents that a model builds its understanding from. Skipping from a first-level heading to a third because the third one looked better at that size is no longer a small sin; it is a false statement about the structure of your argument. Link text stops being something to keep tidy and becomes the only description the destination gets. Alt text, when there are images at all, stops being an accessibility checkbox and becomes the sole existence of the image. And the first sentence of anything you write becomes disproportionately load-bearing, because it is the part most likely to survive summarisation intact. None of this is new advice. It is the advice that accessibility practitioners have been giving, patiently and largely unheeded, for twenty-five years. The interesting development is not that the advice changed. It is that the incentive finally did. Writing a page a screen reader can navigate and writing a page a model can understand turn out to be very nearly the same task, and the second one has a budget attached. ### Against the image I have taken the constraint further on this site than most people would want to, and removed images entirely. The reasoning is not that images are bad. It is that an image is a place where information can hide. Every diagram I have ever put in a post was a diagram I made because the prose underneath it was not doing its job, and the image let me avoid noticing. A screenshot of an error message is a way of not writing the error message down. A chart is a way of not stating the finding. When the picture is removed, the sentence has to be written, and the sentence is what everything downstream — the search index, the summariser, the person reading on a slow connection, the person reading with their eyes closed — actually receives. There is a cost. Some things genuinely are easier to see than to describe, and I will occasionally lose that. I have decided I would rather pay it than keep the escape hatch, because the escape hatch is the thing I reach for exactly when the writing is weakest. ### The part that is still for people None of this argues for austerity. A page can be entirely plain text and still be beautiful, and I think the constraint makes the typography more interesting rather than less, because typography is suddenly the only visual instrument left. Measure, leading, the scale between a heading and its body, the amount of air between one thought and the next — these are the whole toolkit now, and a toolkit of five things used seriously beats a toolkit of fifty used carelessly. So the design of this site varies from one piece to the next, and it varies according to the writing itself: how long the paragraphs run, how often the argument breaks for a heading, how much of it is prose and how much is code. Two essays of similar length can look nothing alike, because they are not shaped alike. But the markup underneath never changes. Every post here emits the same elements in the same order, so that anything reading it structurally sees one consistent document, over and over, no matter what it looks like. The variation is entirely in the stylesheet, which is precisely the layer that the machine discards. That seems to me the right place to put it. Let the design be for the reader who can see. Let the structure be for the reader who cannot. Neither has to be compromised for the other, so long as you are honest about which layer you are working in.