1<!DOCTYPE html><html lang="en"><head> 2 <meta charset="UTF-8"> 3 <link rel="icon" type="image/png" href="/favicon.png"> 4 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 5 <meta name="description" content="PrettyWhale.ai generates your data pipelines with its tests, quality checks, schemas, orchestration and docs. Production-ready, portable, yours to deploy."> 6 <link rel="canonical" href="https://prettywhale.ai/"> 7 8 <!-- Open Graph --> 9 <meta property="og:title" content="Generate Data Pipelines You Trust | PrettyWhale.ai"> 10 <meta property="og:description" content="PrettyWhale.ai generates your data pipelines with its tests, quality checks, schemas, orchestration and docs. Production-ready, portable, yours to deploy."> 11 <meta property="og:url" content="https://prettywhale.ai/"> 12 <!-- Raster only: social platforms drop image/svg+xml. Kept in sync with OG_IMAGE in seo.service.ts. --> 13 <meta property="og:image" content="https://prettywhale.ai/assets/og-default.png"> 14 <meta property="og:image:width" content="1200"> 15 <meta property="og:image:height" content="630"> 16 <meta property="og:image:alt" content="PrettyWhale.ai â ingestion code you trust"> 17 <meta property="og:type" content="website"> 18 <meta property="og:site_name" content="PrettyWhale.ai"> 19 20 <!-- Twitter Card --> 21 <meta name="twitter:card" content="summary_large_image"> 22 <meta name="twitter:title" content="Generate Data Pipelines You Trust | PrettyWhale.ai"> 23 <meta name="twitter:description" content="PrettyWhale.ai generates your data pipelines with its tests, quality checks, schemas, orchestration and docs. Production-ready, portable, yours to deploy."> 24 <meta name="twitter:image" content="https://prettywhale.ai/assets/og-default.png"> 25 <meta name="twitter:image:alt" content="PrettyWhale.ai â ingestion code you trust"> 26 27 <title>Generate Data Pipelines You Trust | PrettyWhale.ai</title> 28 29 <!-- Structured data (Organization + WebSite) --> 30
30<script type="application/ld+json"> 31 { 32 "@context": "https://schema.org", 33 "@graph": [ 34 { 35 "@type": "Organization", 36 "@id": "https://prettywhale.ai/#organization", 37 "name": "PrettyWhale.ai", 38 "alternateName": "PrettyWhale", 39 "legalName": "PrettyWhale.ai", 40 "url": "https://prettywhale.ai", 41 "logo": "https://prettywhale.ai/assets/logo-pw.png", 42 "description": "Engineering AI that generates data ingestion and integration code (complete data pipelines), with its tests, quality checks, schemas, orchestration and documentation.", 43 "email": "[email protected]", 44 "vatID": "FR25994801223", 45 "identifier": { 46 "@type": "PropertyValue", 47 "name": "SIRET", 48 "value": "99480122300011" 49 }, 50 "address": { 51 "@type": "PostalAddress", 52 "streetAddress": "49 rue de Ponthieu", 53 "postalCode": "75008", 54 "addressLocality": "Paris", 55 "addressCountry": "FR" 56 }, 57 "sameAs": ["https://www.linkedin.com/company/prettywhale-ai/"], 58 "knowsAbout": [ 59 "Data ingestion", 60 "Data integration", 61 "Data pipelines", 62 "Data quality", 63 "Code generation", 64 "Small language models", 65 "Engineering AI" 66 ] 67 }, 68 { 69 "@type": "WebSite", 70 "@id": "https://prettywhale.ai/#website", 71 "name": "PrettyWhale.ai", 72 "alternateName": "PrettyWhale", 73 "url": "https://prettywhale.ai", 74 "publisher": { "@id": "https://prettywhale.ai/#organization" } 75 } 76 ] 77 } 78 </script>
78 79 80 <link rel="preconnect" href="https://fonts.googleapis.com"> 81 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin=""> 82 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Poppins:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> 83
83<script type="module" crossorigin="" src="/assets/index-BZ4Q57Vd.js"></script>
83 84 <link rel="stylesheet" crossorigin="" href="/assets/index-DtTAeYv8.css"> 85<style>.header[_ngcontent-ng-c1321049901] { 86 position: sticky; 87 top: 0; 88 z-index: 100; 89 background-color: var(--surface-translucent); 90 backdrop-filter: blur(12px); 91 border-bottom: 1px solid var(--border-alt); 92} 93.header[_ngcontent-ng-c1321049901] .inner[_ngcontent-ng-c1321049901] { 94 display: flex; 95 align-items: center; 96 justify-content: space-between; 97 height: var(--header-height); 98} 99.header[_ngcontent-ng-c1321049901] .logo[_ngcontent-ng-c1321049901] { 100 display: flex; 101 align-items: center; 102 gap: var(--spacing-sm); 103 z-index: 1; 104} 105.header[_ngcontent-ng-c1321049901] .logo-text[_ngcontent-ng-c1321049901] { 106 font-family: var(--font-inter); 107 font-size: 17px; 108 font-weight: 600; 109 color: var(--tertiary); 110} 111.header[_ngcontent-ng-c1321049901] .logo-accent[_ngcontent-ng-c1321049901] { 112 color: var(--primary); 113} 114.header[_ngcontent-ng-c1321049901] .nav[_ngcontent-ng-c1321049901] { 115 display: flex; 116 align-items: center; 117 gap: var(--spacing-xs); 118} 119.header[_ngcontent-ng-c1321049901] .link[_ngcontent-ng-c1321049901] { 120 font-size: var(--font-size-nav); 121 color: var(--on-surface); 122 transition: color var(--transition-fast), background-color var(--transition-fast); 123 background: none; 124 border: 4px solid transparent; 125 cursor: pointer; 126 font-family: inherit; 127 display: inline-flex; 128 align-items: center; 129 justify-content: center; 130 gap: 4px; 131 padding: var(--spacing-xs) var(--spacing-sm); 132 border-radius: var(--border-radius-xs); 133 margin: 0; 134 white-space: nowrap; 135} 136.header[_ngcontent-ng-c1321049901] .link[_ngcontent-ng-c1321049901]:hover { 137 color: var(--primary); 138} 139.header[_ngcontent-ng-c1321049901] .link[_ngcontent-ng-c1321049901]:focus-visible { 140 outline: 2px solid var(--border-focus); 141 outline-offset: 2px; 142} 143.header[_ngcontent-ng-c1321049901] .resources[_ngcontent-ng-c1321049901] { 144 position: relative; 145} 146.header[_ngcontent-ng-c1321049901] .resources-heading[_ngcontent-ng-c1321049901] { 147 display: none; 148} 149.header[_ngcontent-ng-c1321049901] .icon-chevron[_ngcontent-ng-c1321049901], 150.header[_ngcontent-ng-c1321049901] .icon-editor[_ngcontent-ng-c1321049901], 151.header[_ngcontent-ng-c1321049901] .icon-arrow[_ngcontent-ng-c1321049901] { 152 background-color: currentColor; 153 mask-size: contain; 154 mask-repeat: no-repeat; 155 mask-position: center; 156} 157.header[_ngcontent-ng-c1321049901] .icon-chevron[_ngcontent-ng-c1321049901] { 158 width: 12px; 159 height: 12px; 160 mask-image: url("/assets/icons/chevron-right.svg"); 161 rotate: 90deg; 162 transition: rotate var(--transition-fast); 163} 164.header[_ngcontent-ng-c1321049901] .icon-editor[_ngcontent-ng-c1321049901] { 165 width: 15px; 166 height: 15px; 167 mask-image: url("/assets/icons/vscode.svg"); 168} 169.header[_ngcontent-ng-c1321049901] .icon-arrow[_ngcontent-ng-c1321049901] { 170 width: 12px; 171 height: 12px; 172 mask-image: url("/assets/icons/arrow-diagonal.svg"); 173} 174.header[_ngcontent-ng-c1321049901] .resources.open[_ngcontent-ng-c1321049901] .icon-chevron[_ngcontent-ng-c1321049901] { 175 rotate: -90deg; 176} 177.header[_ngcontent-ng-c1321049901] .resources-menu[_ngcontent-ng-c1321049901] { 178 position: absolute; 179 top: calc(100% + var(--spacing-2xs)); 180 left: 0; 181 display: none; 182 flex-direction: column; 183 min-width: 180px; 184 padding: var(--spacing-xs); 185 border-radius: var(--border-radius-sm); 186 background-color: var(--surface-container); 187 box-shadow: 0 6px 6px rgba(0, 0, 0, 0.25); 188 list-style: none; 189} 190.header[_ngcontent-ng-c1321049901] .resources.open[_ngcontent-ng-c1321049901] .resources-menu[_ngcontent-ng-c1321049901] { 191 display: flex; 192} 193.header[_ngcontent-ng-c1321049901] .resources-link[_ngcontent-ng-c1321049901] { 194 justify-content: flex-start; 195 width: 100%; 196 padding: var(--spacing-sm) var(--spacing-md); 197 border: none; 198 white-space: nowrap; 199} 200.header[_ngcontent-ng-c1321049901] .install[_ngcontent-ng-c1321049901], 201.header[_ngcontent-ng-c1321049901] .contact[_ngcontent-ng-c1321049901] { 202 font-size: var(--font-size-sm); 203 font-weight: 600; 204 white-space: nowrap; 205 padding: var(--spacing-xs) var(--spacing-md); 206 border-radius: var(--border-radius-xs); 207 transition: opacity var(--transition-fast); 208} 209.header[_ngcontent-ng-c1321049901] .install[_ngcontent-ng-c1321049901]:hover, 210.header[_ngcontent-ng-c1321049901] .contact[_ngcontent-ng-c1321049901]:hover { 211 opacity: 0.9; 212} 213.header[_ngcontent-ng-c1321049901] .install[_ngcontent-ng-c1321049901] { 214 display: inline-flex; 215 align-items: center; 216 gap: var(--spacing-2xs); 217 border: 4px solid var(--primary-border); 218 background-color: var(--primary); 219 color: var(--const-white); 220} 221.header[_ngcontent-ng-c1321049901] .contact[_ngcontent-ng-c1321049901] { 222 border: 4px solid var(--secondary-border); 223 background-color: var(--secondary); 224 color: var(--on-surface); 225} 226.header[_ngcontent-ng-c1321049901] .burger[_ngcontent-ng-c1321049901] { 227 display: none; 228 flex-direction: column; 229 justify-content: center; 230 gap: 6px; 231 width: 32px; 232 height: 32px; 233 padding: 4px; 234 background: none; 235 border: none; 236 cursor: pointer; 237 z-index: 1; 238} 239.header[_ngcontent-ng-c1321049901] .burger-line[_ngcontent-ng-c1321049901] { 240 display: block; 241 width: 100%; 242 height: 3px; 243 background-color: var(--on-surface); 244 border-radius: 1px; 245 transition: transform 0.3s, opacity 0.3s; 246} 247.header[_ngcontent-ng-c1321049901] .burger[aria-expanded=true][_ngcontent-ng-c1321049901] .burger-line[_ngcontent-ng-c1321049901]:nth-child(1) { 248 transform: translateY(9px) rotate(45deg); 249} 250.header[_ngcontent-ng-c1321049901] .burger[aria-expanded=true][_ngcontent-ng-c1321049901] .burger-line[_ngcontent-ng-c1321049901]:nth-child(2) { 251 opacity: 0; 252} 253.header[_ngcontent-ng-c1321049901] .burger[aria-expanded=true][_ngcontent-ng-c1321049901] .burger-line[_ngcontent-ng-c1321049901]:nth-child(3) { 254 transform: translateY(-9px) rotate(-45deg); 255} 256@media (max-width: 768px) { 257 .header[_ngcontent-ng-c1321049901] { 258 transition: transform 0.3s ease; 259 } 260 .header.hidden[_ngcontent-ng-c1321049901] { 261 transform: translateY(-100%); 262 } 263 .header[_ngcontent-ng-c1321049901] .burger[_ngcontent-ng-c1321049901] { 264 display: flex; 265 } 266 .header[_ngcontent-ng-c1321049901] .nav[_ngcontent-ng-c1321049901] { 267 position: fixed; 268 top: var(--header-height); 269 left: 0; 270 right: 0; 271 flex-direction: column; 272 align-items: stretch; 273 gap: 0; 274 background-color: var(--surface); 275 border-bottom: 1px solid var(--border); 276 padding: var(--spacing-sm) 0; 277 display: none; 278 } 279 .header[_ngcontent-ng-c1321049901] .nav.visible[_ngcontent-ng-c1321049901] { 280 display: flex; 281 opacity: 0; 282 transform: translateY(-8px); 283 transition: opacity 0.3s, transform 0.3s; 284 }
285 .header[_ngcontent-ng-c1321049901] .nav.visible.open[_ngcontent-ng-c1321049901] { 286 opacity: 1; 287 transform: translateY(0); 288 } 289 .header[_ngcontent-ng-c1321049901] .link[_ngcontent-ng-c1321049901] { 290 font-size: var(--font-size-base); 291 margin: 0; 292 padding: var(--spacing-sm) var(--spacing-lg); 293 border: none; 294 border-radius: 0; 295 justify-content: flex-start; 296 } 297 .header[_ngcontent-ng-c1321049901] .link[_ngcontent-ng-c1321049901]:hover { 298 background-color: var(--surface-container); 299 } 300 .header[_ngcontent-ng-c1321049901] .resources-toggle[_ngcontent-ng-c1321049901] { 301 display: none; 302 } 303 .header[_ngcontent-ng-c1321049901] .resources-heading[_ngcontent-ng-c1321049901] { 304 display: block; 305 padding: var(--spacing-sm) var(--spacing-lg) var(--spacing-2xs); 306 font-size: var(--font-size-xs); 307 font-weight: 600; 308 letter-spacing: 0.08em; 309 text-transform: uppercase; 310 color: var(--on-surface-container); 311 } 312 .header[_ngcontent-ng-c1321049901] .resources-menu[_ngcontent-ng-c1321049901] { 313 position: static; 314 display: flex; 315 min-width: 0; 316 padding: 0; 317 border-radius: 0; 318 background: none; 319 box-shadow: none; 320 } 321 .header[_ngcontent-ng-c1321049901] .resources-link[_ngcontent-ng-c1321049901] { 322 padding-left: var(--spacing-2xl); 323 } 324 .header[_ngcontent-ng-c1321049901] .install[_ngcontent-ng-c1321049901] { 325 justify-content: center; 326 margin: var(--spacing-sm) var(--spacing-lg) 0; 327 } 328 .header[_ngcontent-ng-c1321049901] .contact[_ngcontent-ng-c1321049901] { 329 margin: var(--spacing-sm) var(--spacing-lg); 330 text-align: center; 331 } 332}</style><style>.hero[_ngcontent-ng-c2863248315] { 333 position: relative; 334 min-height: calc(100vh - var(--header-height)); 335 padding: var(--spacing-4xl) 0 var(--spacing-3xl); 336 background: var(--surface); 337 cursor: default; 338} 339.hero[_ngcontent-ng-c2863248315] .inner[_ngcontent-ng-c2863248315] { 340 position: relative; 341 z-index: 1; 342 display: grid; 343 grid-template-columns: 1fr 1fr; 344 gap: var(--spacing-2xl); 345 align-items: center; 346 min-height: calc(840px - var(--spacing-4xl) - var(--spacing-3xl)); 347} 348.hero[_ngcontent-ng-c2863248315] .title[_ngcontent-ng-c2863248315] { 349 font-size: var(--font-size-4xl); 350 font-weight: 700; 351 line-height: var(--line-height-tight); 352 margin-bottom: var(--spacing-lg); 353 color: var(--tertiary); 354} 355.hero[_ngcontent-ng-c2863248315] .gradient[_ngcontent-ng-c2863248315] { 356 background: linear-gradient(90deg, var(--linear-1), var(--linear-2)); 357 background-clip: text; 358 -webkit-background-clip: text; 359 -webkit-text-fill-color: transparent; 360} 361.hero[_ngcontent-ng-c2863248315] .subtitle[_ngcontent-ng-c2863248315] { 362 font-family: var(--font-inter); 363 font-size: var(--font-size-lg); 364 color: var(--tertiary); 365 font-weight: 500; 366 max-width: 100%; 367 margin-bottom: var(--spacing-xl); 368} 369.hero[_ngcontent-ng-c2863248315] .actions[_ngcontent-ng-c2863248315] { 370 display: flex; 371 align-items: center; 372 gap: var(--spacing-sm); 373} 374.hero[_ngcontent-ng-c2863248315] .install-link[_ngcontent-ng-c2863248315] { 375 display: inline-flex; 376 align-items: center; 377 gap: var(--spacing-xs); 378 white-space: nowrap; 379 padding: var(--spacing-sm) var(--spacing-lg); 380 border-radius: var(--border-radius-sm); 381 border: 4px solid var(--primary-border); 382 background-color: var(--primary); 383 color: var(--const-white); 384 font-weight: 600; 385 font-size: var(--font-size-base); 386 text-decoration: none; 387 transition: opacity var(--transition-fast); 388} 389.hero[_ngcontent-ng-c2863248315] .install-link[_ngcontent-ng-c2863248315]:hover { 390 opacity: 0.9; 391} 392.hero[_ngcontent-ng-c2863248315] .contact-link[_ngcontent-ng-c2863248315] { 393 display: inline-flex; 394 align-items: center; 395 white-space: nowrap; 396 padding: var(--spacing-sm) var(--spacing-lg); 397 border-radius: var(--border-radius-sm); 398 border: 4px solid var(--secondary-border); 399 background-color: var(--secondary); 400 color: var(--on-surface); 401 font-weight: 600; 402 font-size: var(--font-size-base); 403 text-decoration: none; 404 transition: opacity var(--transition-fast); 405} 406.hero[_ngcontent-ng-c2863248315] .contact-link[_ngcontent-ng-c2863248315]:hover { 407 opacity: 0.9; 408} 409.hero[_ngcontent-ng-c2863248315] .icon-arrow[_ngcontent-ng-c2863248315],
410.hero[_ngcontent-ng-c2863248315] .icon-editor[_ngcontent-ng-c2863248315] { 411 display: inline-block; 412 width: 16px; 413 height: 16px; 414 background-color: currentColor; 415 mask-size: contain; 416 mask-repeat: no-repeat; 417 mask-position: center; 418} 419.hero[_ngcontent-ng-c2863248315] .icon-arrow[_ngcontent-ng-c2863248315] { 420 mask-image: url("/assets/icons/arrow-diagonal.svg"); 421} 422.hero[_ngcontent-ng-c2863248315] .icon-editor[_ngcontent-ng-c2863248315] { 423 width: 18px; 424 height: 18px; 425 mask-image: url("/assets/icons/vscode.svg"); 426} 427.hero[_ngcontent-ng-c2863248315] .visual[_ngcontent-ng-c2863248315] { 428 display: flex; 429 justify-content: center; 430} 431.hero[_ngcontent-ng-c2863248315] .visual[_ngcontent-ng-c2863248315] img[_ngcontent-ng-c2863248315] { 432 width: 100%; 433 height: auto; 434 filter: drop-shadow(-10px 16px 9px rgba(0, 0, 0, 0.25)); 435} 436@media (max-width: 768px) { 437 .hero[_ngcontent-ng-c2863248315] .inner[_ngcontent-ng-c2863248315] { 438 grid-template-columns: 1fr; 439 } 440 .hero[_ngcontent-ng-c2863248315] .title[_ngcontent-ng-c2863248315] { 441 font-size: var(--font-size-xxl); 442 } 443 .hero[_ngcontent-ng-c2863248315] .actions[_ngcontent-ng-c2863248315] { 444 flex-direction: column; 445 align-items: flex-start; 446 } 447}</style><style>.demo-section[_ngcontent-ng-c312145568] { 448 padding: var(--spacing-5xl) 0; 449 background-color: var(--surface-container); 450} 451.demo-section[_ngcontent-ng-c312145568] .heading[_ngcontent-ng-c312145568] { 452 margin-bottom: var(--spacing-2xl); 453} 454.demo-section[_ngcontent-ng-c312145568] .title[_ngcontent-ng-c312145568] { 455 font-size: var(--font-size-3xl); 456 color: var(--on-surface); 457 text-align: left; 458 margin-bottom: var(--spacing-md); 459} 460.demo-section[_ngcontent-ng-c312145568] .subtitle[_ngcontent-ng-c312145568] { 461 font-family: var(--font-inter); 462 font-size: var(--font-size-lg); 463 font-weight: 400; 464 line-height: var(--line-height-relaxed); 465 color: var(--on-surface-container); 466 text-wrap: pretty; 467} 468.demo-section[_ngcontent-ng-c312145568] .subtitle[_ngcontent-ng-c312145568] strong[_ngcontent-ng-c312145568] { 469 font-weight: 600; 470 color: var(--on-surface); 471} 472.demo-section[_ngcontent-ng-c312145568] .preview[_ngcontent-ng-c312145568] video[_ngcontent-ng-c312145568] { 473 width: 100%; 474 display: block; 475 border-radius: var(--border-radius-md); 476} 477@media (max-width: 768px) { 478 .demo-section[_ngcontent-ng-c312145568] .heading[_ngcontent-ng-c312145568] { 479 margin-bottom: var(--spacing-xl); 480 } 481 .demo-section[_ngcontent-ng-c312145568] .subtitle[_ngcontent-ng-c312145568] { 482 font-size: var(--font-size-base); 483 } 484 .demo-section[_ngcontent-ng-c312145568] .preview[_ngcontent-ng-c312145568] { 485 margin-left: calc(-1 * var(--spacing-lg)); 486 margin-right: calc(-1 * var(--spacing-lg)); 487 } 488 .demo-section[_ngcontent-ng-c312145568] .preview[_ngcontent-ng-c312145568] video[_ngcontent-ng-c312145568] { 489 border-radius: 0; 490 } 491}</style><style>.stepper[_ngcontent-ng-c1779684384] { 492 padding: var(--spacing-5xl) 0; 493} 494 495.header[_ngcontent-ng-c1779684384] { 496 display: flex; 497 align-items: center; 498 justify-content: space-between; 499 margin-bottom: var(--spacing-xl); 500} 501 502.title[_ngcontent-ng-c1779684384] { 503 font-size: var(--font-size-3xl); 504 color: var(--on-surface); 505 margin-bottom: var(--spacing-md); 506} 507 508.subtitle[_ngcontent-ng-c1779684384] { 509 font-family: var(--font-inter); 510 font-size: var(--font-size-lg); 511 font-weight: 400; 512 line-height: var(--line-height-relaxed); 513 color: var(--on-surface-container); 514 text-wrap: pretty; 515} 516.subtitle[_ngcontent-ng-c1779684384] strong[_ngcontent-ng-c1779684384] { 517 font-weight: 600; 518 color: var(--on-surface); 519} 520 521.nav[_ngcontent-ng-c1779684384] { 522 display: flex; 523 gap: var(--spacing-xs); 524} 525 526.nav-btn[_ngcontent-ng-c1779684384] { 527 display: flex; 528 align-items: center; 529 justify-content: center; 530 width: 48px; 531 height: 48px; 532 border: 1px solid var(--border-alt); 533 border-radius: 50%; 534 background: transparent; 535 cursor: pointer; 536 transition: opacity var(--transition-fast); 537} 538.nav-btn.disabled[_ngcontent-ng-c1779684384] { 539 opacity: 0.3; 540 cursor: default; 541} 542 543.nav-icon[_ngcontent-ng-c1779684384] { 544 display: block; 545 width: 11px; 546 height: 20px; 547 background-color: var(--on-surface); 548 mask-size: contain; 549 mask-repeat: no-repeat; 550 mask-position: center; 551} 552 553.track[_ngcontent-ng-c1779684384] { 554 --_inset: max(var(--spacing-lg), calc((100% - var(--container-max-width)) / 2 + var(--spacing-lg))); 555 display: flex; 556 gap: var(--spacing-md); 557 overflow-x: auto; 558 scroll-snap-type: x mandatory; 559 scroll-padding-left: var(--_inset); 560 scrollbar-width: none; 561 height: 437px; 562 cursor: grab; 563 user-select: none; 564 padding-left: var(--_inset); 565 padding-right: var(--_inset); 566} 567.track.grabbing[_ngcontent-ng-c1779684384] { 568 cursor: grabbing; 569 scroll-snap-type: none; 570} 571.track[_ngcontent-ng-c1779684384]::-webkit-scrollbar {
572 display: none; 573} 574 575.step[_ngcontent-ng-c1779684384] { 576 flex-shrink: 0; 577 width: 300px; 578 height: var(--step-height); 579 background-color: var(--step-bg); 580 color: var(--step-color); 581 border-radius: var(--border-radius-xs); 582 padding: var(--spacing-md); 583 display: flex; 584 flex-direction: column; 585 justify-content: space-between; 586 overflow: hidden; 587 scroll-snap-align: start; 588 transition: height 0.3s ease; 589 background-repeat: no-repeat; 590 background-position: top right; 591 background-size: auto var(--step-hover-height); 592 font-family: var(--font-inter); 593 line-height: 24px; 594} 595.step[_ngcontent-ng-c1779684384]:hover { 596 height: var(--step-hover-height); 597} 598.step[_ngcontent-ng-c1779684384]:hover .step-subtitle[_ngcontent-ng-c1779684384] { 599 opacity: 0; 600 height: 0; 601 margin: 0; 602} 603.step[_ngcontent-ng-c1779684384]:hover .step-description[_ngcontent-ng-c1779684384] { 604 opacity: 1; 605 height: auto; 606 margin-top: 8px; 607} 608 609.step-badge[_ngcontent-ng-c1779684384] { 610 width: 40px; 611 height: 40px; 612 background-color: var(--surface-container); 613 border-radius: var(--border-radius-sm); 614 display: flex; 615 align-items: center; 616 justify-content: center; 617 font-size: 24px; 618 font-weight: 400; 619 color: var(--on-surface); 620} 621 622.step-content[_ngcontent-ng-c1779684384] { 623 display: flex; 624 flex-direction: column; 625} 626 627.step-title[_ngcontent-ng-c1779684384] { 628 font-size: 16px; 629 font-weight: 500; 630 line-height: 24px; 631} 632 633.step-subtitle[_ngcontent-ng-c1779684384] { 634 font-size: 16px; 635 font-weight: 400; 636 opacity: 1; 637 line-height: 24px; 638 transition: opacity 0.3s ease, height 0.3s ease; 639} 640 641.step-description[_ngcontent-ng-c1779684384] { 642 font-family: var(--font-inter); 643 font-size: 16px; 644 font-weight: 400; 645 line-height: 24px; 646 opacity: 0; 647 height: 0; 648 overflow: hidden; 649 transition: opacity 0.3s ease, height 0.3s ease; 650} 651 652@media (max-width: 768px) { 653 .title[_ngcontent-ng-c1779684384] { 654 font-size: var(--font-size-3xl); 655 } 656 .subtitle[_ngcontent-ng-c1779684384] { 657 font-size: var(--font-size-base); 658 } 659 .step[_ngcontent-ng-c1779684384] { 660 width: 260px; 661 height: var(--step-hover-height); 662 } 663 .step[_ngcontent-ng-c1779684384] .step-subtitle[_ngcontent-ng-c1779684384] { 664 opacity: 0; 665 height: 0; 666 margin: 0; 667 } 668 .step[_ngcontent-ng-c1779684384] .step-description[_ngcontent-ng-c1779684384] { 669 opacity: 1; 670 height: auto; 671 margin-top: 8px; 672 } 673}</style><style>.focus-tabs[_ngcontent-ng-c2440753150] { 674 padding: var(--spacing-5xl) 0; 675 background-color: var(--surface-container); 676} 677.focus-tabs[_ngcontent-ng-c2440753150] .title[_ngcontent-ng-c2440753150] { 678 font-size: var(--font-size-3xl); 679 margin-bottom: var(--spacing-2xl); 680} 681.focus-tabs[_ngcontent-ng-c2440753150] .layout[_ngcontent-ng-c2440753150] { 682 display: grid; 683 grid-template-columns: 500fr 668fr; 684 gap: var(--spacing-lg); 685 align-items: start; 686} 687.focus-tabs[_ngcontent-ng-c2440753150] .tabs[_ngcontent-ng-c2440753150] { 688 display: flex; 689 flex-direction: column; 690 gap: var(--spacing-sm); 691} 692.focus-tabs[_ngcontent-ng-c2440753150] .tab[_ngcontent-ng-c2440753150] { 693 display: flex; 694 align-items: center; 695 gap: var(--spacing-md); 696 padding: var(--spacing-md) var(--spacing-lg); 697 border: 1px solid transparent; 698 border-radius: var(--border-radius-xs); 699 background: none; 700 cursor: pointer; 701 text-align: left; 702 font-family: var(--font-body); 703 transition: background-color var(--transition-fast), border-color var(--transition-fast); 704} 705.focus-tabs[_ngcontent-ng-c2440753150] .tab[_ngcontent-ng-c2440753150]:hover { 706 background-color: var(--surface); 707} 708.focus-tabs[_ngcontent-ng-c2440753150] .tab.active[_ngcontent-ng-c2440753150] { 709 background-color: var(--surface); 710 border-color: var(--border-alt); 711} 712.focus-tabs[_ngcontent-ng-c2440753150] .tab.active[_ngcontent-ng-c2440753150] .tab-icon[_ngcontent-ng-c2440753150] { 713 background-color: var(--primary); 714} 715.focus-tabs[_ngcontent-ng-c2440753150] .tab.active[_ngcontent-ng-c2440753150] .tab-title[_ngcontent-ng-c2440753150] { 716 color: var(--primary); 717} 718.focus-tabs[_ngcontent-ng-c2440753150] .tab.active[_ngcontent-ng-c2440753150] .tab-description[_ngcontent-ng-c2440753150] { 719 color: var(--on-surface); 720} 721.focus-tabs[_ngcontent-ng-c2440753150] .tab-icon[_ngcontent-ng-c2440753150] { 722 flex-shrink: 0; 723 width: 24px; 724 height: 24px; 725 background-color: var(--on-surface); 726 mask-size: contain; 727 mask-repeat: no-repeat; 728 mask-position: center; 729 transition: background-color var(--transition-fast); 730} 731.focus-tabs[_ngcontent-ng-c2440753150] .tab-title[_ngcontent-ng-c2440753150] { 732 display: block; 733 font-size: 16px; 734 font-weight: 500; 735 color: var(--on-surface); 736 transition: color var(--transition-fast); 737} 738.focus-tabs[_ngcontent-ng-c2440753150] .tab-description[_ngcontent-ng-c2440753150] { 739 display: block; 740 font-size: 16px; 741 color: var(--on-surface-container); 742 margin-top: 2px; 743 font-family: var(--font-inter); 744 line-height: 24px; 745} 746.focus-tabs[_ngcontent-ng-c2440753150] .btn-download[_ngcontent-ng-c2440753150] { 747 display: inline-flex; 748 align-items: center; 749 justify-content: center; 750 white-space: nowrap; 751 margin-top: var(--spacing-md); 752 padding: var(--spacing-sm) var(--spacing-lg); 753 border-radius: var(--border-radius-sm); 754 border: 4px solid var(--primary-border); 755 background-color: var(--primary); 756 color: var(--surface); 757 font-weight: 600; 758 font-size: var(--font-size-base); 759 transition: opacity var(--transition-fast); 760} 761.focus-tabs[_ngcontent-ng-c2440753150] .btn-download[_ngcontent-ng-c2440753150]:hover { 762 opacity: 0.9; 763} 764.focus-tabs[_ngcontent-ng-c2440753150] .panel[_ngcontent-ng-c2440753150] { 765 background-color: var(--tertiary); 766 border-radius: var(--border-radius-xs); 767 position: relative; 768 overflow: hidden; 769 align-self: stretch; 770} 771.focus-tabs[_ngcontent-ng-c2440753150] .tab-panel[_ngcontent-ng-c2440753150] { 772 display: none; 773 background-color: var(--tertiary); 774 border-radius: var(--border-radius-xs); 775 position: relative; 776 overflow: hidden; 777 aspect-ratio: 668/580; 778} 779.focus-tabs[_ngcontent-ng-c2440753150] .frame[_ngcontent-ng-c2440753150] { 780 position: absolute; 781 inset: 7% 0 0 5%; 782 border-radius: var(--border-radius-md) 0 0 0; 783 overflow: hidden; 784 box-shadow: -16px 16px 8px 0 rgba(0, 0, 0, 0.25); 785} 786.focus-tabs[_ngcontent-ng-c2440753150] .panel-image[_ngcontent-ng-c2440753150], 787.focus-tabs[_ngcontent-ng-c2440753150] .tab-image[_ngcontent-ng-c2440753150] { 788 position: absolute; 789 top: 0; 790 left: 0; 791 display: block; 792 width: 100%; 793 height: 100%; 794 object-fit: cover; 795 object-position: left bottom; 796} 797@media (max-width: 768px) { 798 .focus-tabs[_ngcontent-ng-c2440753150] .layout[_ngcontent-ng-c2440753150] { 799 grid-template-columns: 1fr; 800 } 801 .focus-tabs[_ngcontent-ng-c2440753150] .tabs[_ngcontent-ng-c2440753150] { 802 gap: var(--spacing-2xl); 803 } 804 .focus-tabs[_ngcontent-ng-c2440753150] .tab-image[_ngcontent-ng-c2440753150] { 805 object-position: left top; 806 transform: scale(1.9); 807 transform-origin: top left; 808 } 809 .focus-tabs[_ngcontent-ng-c2440753150] .tab-block[_ngcontent-ng-c2440753150] { 810 display: flex; 811 flex-direction: column; 812 gap: var(--spacing-md); 813 } 814 .focus-tabs[_ngcontent-ng-c2440753150] .tab-icon[_ngcontent-ng-c2440753150] { 815 display: none; 816 } 817 .focus-tabs[_ngcontent-ng-c2440753150] .tab[_ngcontent-ng-c2440753150] { 818 padding: 0; 819 cursor: default; 820 pointer-events: none; 821 } 822 .focus-tabs[_ngcontent-ng-c2440753150] .tab[_ngcontent-ng-c2440753150]:hover { 823 background-color: transparent; 824 } 825 .focus-tabs[_ngcontent-ng-c2440753150] .tab.active[_ngcontent-ng-c2440753150] { 826 background-color: transparent; 827 border-color: transparent; 828 } 829 .focus-tabs[_ngcontent-ng-c2440753150] .tab.active[_ngcontent-ng-c2440753150] .tab-icon[_ngcontent-ng-c2440753150] { 830 background-color: var(--on-surface); 831 } 832 .focus-tabs[_ngcontent-ng-c2440753150] .tab.active[_ngcontent-ng-c2440753150] .tab-title[_ngcontent-ng-c2440753150] { 833 color: var(--on-surface); 834 } 835 .focus-tabs[_ngcontent-ng-c2440753150] .tab-panel[_ngcontent-ng-c2440753150] { 836 display: block; 837 } 838 .focus-tabs[_ngcontent-ng-c2440753150] .panel[_ngcontent-ng-c2440753150] { 839 display: none; 840 } 841}</style><style>.partners-preview[_ngcontent-ng-c3308003094] { 842 padding: var(--spacing-5xl) 0; 843} 844.partners-preview[_ngcontent-ng-c3308003094] .header[_ngcontent-ng-c3308003094] { 845 display: flex; 846 justify-content: space-between; 847 align-items: center; 848 margin-bottom: var(--spacing-2xl); 849} 850.partners-preview[_ngcontent-ng-c3308003094] .title[_ngcontent-ng-c3308003094] { 851 font-size: var(--font-size-3xl); 852} 853.partners-preview[_ngcontent-ng-c3308003094] .link[_ngcontent-ng-c3308003094] { 854 display: inline-flex; 855 align-items: center; 856 gap: var(--spacing-xs); 857 font-size: var(--font-size-base); 858 font-weight: 600; 859 color: var(--on-surface); 860 transition: color var(--transition-fast); 861} 862.partners-preview[_ngcontent-ng-c3308003094] .link[_ngcontent-ng-c3308003094]:hover {
863 color: var(--primary); 864} 865.partners-preview[_ngcontent-ng-c3308003094] .link[_ngcontent-ng-c3308003094]:focus-visible { 866 outline: 2px solid var(--border-focus); 867 outline-offset: 2px; 868 border-radius: var(--border-radius-xs); 869} 870.partners-preview[_ngcontent-ng-c3308003094] .icon-arrow[_ngcontent-ng-c3308003094] { 871 display: inline-block; 872 width: 16px; 873 height: 16px; 874 background-color: currentColor; 875 mask-image: url("/assets/icons/arrow-diagonal.svg"); 876 mask-size: contain; 877 mask-repeat: no-repeat; 878} 879@media (max-width: 768px) { 880 .partners-preview[_ngcontent-ng-c3308003094] .header[_ngcontent-ng-c3308003094] { 881 flex-direction: column; 882 align-items: flex-start; 883 gap: var(--spacing-sm); 884 margin-bottom: var(--spacing-xl); 885 } 886}</style><style>.blog-preview[_ngcontent-ng-c4283817964] { 887 padding: var(--spacing-5xl) 0; 888 background-color: var(--surface-container); 889} 890.blog-preview[_ngcontent-ng-c4283817964] .header[_ngcontent-ng-c4283817964] { 891 display: flex; 892 justify-content: space-between; 893 align-items: center; 894 margin-bottom: var(--spacing-2xl); 895} 896.blog-preview[_ngcontent-ng-c4283817964] .title[_ngcontent-ng-c4283817964] { 897 font-size: var(--font-size-3xl); 898} 899.blog-preview[_ngcontent-ng-c4283817964] .link[_ngcontent-ng-c4283817964] { 900 display: inline-flex; 901 align-items: center; 902 gap: var(--spacing-xs); 903 font-size: var(--font-size-base); 904 font-weight: 600; 905 color: var(--on-surface); 906 transition: color var(--transition-fast); 907} 908.blog-preview[_ngcontent-ng-c4283817964] .link[_ngcontent-ng-c4283817964]:hover { 909 color: var(--primary); 910} 911.blog-preview[_ngcontent-ng-c4283817964] .icon-arrow[_ngcontent-ng-c4283817964] { 912 display: inline-block; 913 width: 16px; 914 height: 16px; 915 background-color: currentColor; 916 mask-image: url("/assets/icons/arrow-diagonal.svg"); 917 mask-size: contain; 918 mask-repeat: no-repeat; 919} 920.blog-preview[_ngcontent-ng-c4283817964] .grid[_ngcontent-ng-c4283817964] { 921 display: grid; 922 grid-template-columns: repeat(3, minmax(0, 1fr)); 923 gap: var(--spacing-lg); 924} 925@media (min-width: 769px) { 926 .blog-preview[_ngcontent-ng-c4283817964] .grid[_ngcontent-ng-c4283817964] > pw-post-card[_ngcontent-ng-c4283817964]:nth-child(n+4) { 927 display: none; 928 } 929} 930@media (max-width: 768px) { 931 .blog-preview[_ngcontent-ng-c4283817964] .link[_ngcontent-ng-c4283817964] { 932 display: none; 933 } 934 .blog-preview[_ngcontent-ng-c4283817964] .grid[_ngcontent-ng-c4283817964] { 935 display: flex; 936 flex-direction: row; 937 gap: var(--spacing-md); 938 overflow-x: auto; 939 scroll-snap-type: x mandatory; 940 -webkit-overflow-scrolling: touch; 941 scrollbar-width: none; 942 } 943 .blog-preview[_ngcontent-ng-c4283817964] .grid[_ngcontent-ng-c4283817964]::-webkit-scrollbar {
944 display: none; 945 } 946 .blog-preview[_ngcontent-ng-c4283817964] .grid[_ngcontent-ng-c4283817964] pw-post-card[_ngcontent-ng-c4283817964] { 947 flex: 0 0 80%; 948 max-width: 300px; 949 scroll-snap-align: start; 950 } 951}</style><style>.get-started[_ngcontent-ng-c4078681124] .container[_ngcontent-ng-c4078681124] { 952 display: flex; 953 flex-direction: column; 954 align-items: center; 955 justify-content: center; 956 height: 400px; 957 gap: 48px; 958} 959.get-started[_ngcontent-ng-c4078681124] .title[_ngcontent-ng-c4078681124] { 960 font-size: 48px; 961 font-weight: 500; 962 text-align: center; 963 color: var(--on-surface); 964} 965.get-started[_ngcontent-ng-c4078681124] .btn[_ngcontent-ng-c4078681124] { 966 display: inline-flex; 967 align-items: center; 968 justify-content: center; 969 padding: var(--spacing-sm) var(--spacing-lg); 970 border-radius: var(--border-radius-sm); 971 border: 4px solid var(--primary-border); 972 background-color: var(--primary); 973 color: var(--const-white); 974 font-weight: 600; 975 font-size: var(--font-size-base); 976 text-decoration: none; 977 transition: opacity var(--transition-fast); 978} 979.get-started[_ngcontent-ng-c4078681124] .btn[_ngcontent-ng-c4078681124]:hover { 980 opacity: 0.9; 981} 982@media (max-width: 768px) { 983 .get-started[_ngcontent-ng-c4078681124] .title[_ngcontent-ng-c4078681124] { 984 font-size: var(--font-size-3xl); 985 } 986}</style><style>.footer[_ngcontent-ng-c1940239115] { 987 background-color: var(--const-black); 988 padding: var(--spacing-3xl) 0 var(--spacing-xl); 989} 990.footer[_ngcontent-ng-c1940239115] .links[_ngcontent-ng-c1940239115] { 991 display: flex; 992 align-items: flex-start; 993 justify-content: center; 994 gap: var(--spacing-3xl); 995 margin-top: var(--spacing-2xl); 996} 997.footer[_ngcontent-ng-c1940239115] .col[_ngcontent-ng-c1940239115] { 998 display: flex; 999 flex-direction: column; 1000 gap: var(--spacing-xs); 1001} 1002.footer[_ngcontent-ng-c1940239115] .col-title[_ngcontent-ng-c1940239115] { 1003 font-size: var(--font-size-base); 1004 font-weight: 600; 1005 color: var(--const-white); 1006 margin-bottom: var(--spacing-xs); 1007} 1008.footer[_ngcontent-ng-c1940239115] .link[_ngcontent-ng-c1940239115] { 1009 font-size: var(--font-size-nav); 1010 color: var(--const-grey); 1011 transition: color var(--transition-fast); 1012} 1013.footer[_ngcontent-ng-c1940239115] .link[_ngcontent-ng-c1940239115]:hover { 1014 color: var(--const-white); 1015} 1016.footer[_ngcontent-ng-c1940239115] .social-link[_ngcontent-ng-c1940239115] { 1017 display: flex; 1018 color: var(--const-grey); 1019 transition: color var(--transition-fast); 1020} 1021.footer[_ngcontent-ng-c1940239115] .social-link[_ngcontent-ng-c1940239115]:hover { 1022 color: var(--const-white); 1023} 1024.footer[_ngcontent-ng-c1940239115] .social-link[_ngcontent-ng-c1940239115] .icon[_ngcontent-ng-c1940239115] { 1025 display: block; 1026 width: 20px; 1027 height: 20px; 1028 background-color: currentColor; 1029 mask-image: url("/assets/icons/linkedin.svg"); 1030 mask-size: contain; 1031 mask-repeat: no-repeat; 1032 mask-position: center; 1033} 1034.footer[_ngcontent-ng-c1940239115] .copyright[_ngcontent-ng-c1940239115] { 1035 margin-top: var(--spacing-2xl); 1036 text-align: center; 1037 font-size: 12px; 1038 font-weight: 300; 1039 color: var(--const-grey); 1040} 1041.footer[_ngcontent-ng-c1940239115] .email[_ngcontent-ng-c1940239115] { 1042 font-weight: 400; 1043 color: var(--const-grey); 1044} 1045.footer[_ngcontent-ng-c1940239115] .email[_ngcontent-ng-c1940239115]:hover { 1046 color: var(--const-white); 1047} 1048@media (max-width: 768px) { 1049 .footer[_ngcontent-ng-c1940239115] .links[_ngcontent-ng-c1940239115] { 1050 flex-wrap: wrap; 1051 gap: var(--spacing-lg) var(--spacing-xl); 1052 } 1053 .footer[_ngcontent-ng-c1940239115] .link[_ngcontent-ng-c1940239115] { 1054 white-space: nowrap; 1055 } 1056}</style><style>[_nghost-ng-c1482773486] { 1057 display: block; 1058 position: absolute; 1059 inset: 0; 1060 overflow: hidden; 1061 } 1062 1063 canvas[_ngcontent-ng-c1482773486] { 1064 width: 100%; 1065 height: 100%; 1066 background: var(--surface); 1067 }</style><style>.partners[_ngcontent-ng-c3721682887] { 1068 --cols: 5; 1069 display: grid; 1070 grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); 1071 gap: var(--spacing-xl); 1072} 1073.partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887] { 1074 position: relative; 1075 display: block; 1076 width: 100%; 1077 height: 56px; 1078 padding: 0; 1079 border: none; 1080 background: none; 1081 cursor: pointer; 1082 opacity: 0.55; 1083 transition: opacity var(--transition-fast); 1084} 1085.partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887]:hover, .partners[_ngcontent-ng-c3721682887] .logo.active[_ngcontent-ng-c3721682887] { 1086 opacity: 1; 1087} 1088.partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887]:focus-visible { 1089 outline: 2px solid var(--border-focus); 1090 outline-offset: 6px; 1091 border-radius: var(--border-radius-xs); 1092} 1093.partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887]::after {
1094 content: ""; 1095 position: absolute; 1096 left: 0; 1097 right: 0; 1098 bottom: calc(-1 * var(--spacing-md)); 1099 height: 2px; 1100 background-color: var(--primary); 1101 scale: 0 1; 1102 transition: scale var(--transition-fast); 1103} 1104.partners[_ngcontent-ng-c3721682887] .logo.active[_ngcontent-ng-c3721682887]::after { 1105 scale: 1 1; 1106} 1107.partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887] img[_ngcontent-ng-c3721682887] { 1108 object-fit: contain; 1109 filter: grayscale(1) invert(1); 1110 mix-blend-mode: screen; 1111 transition: filter var(--transition-fast); 1112} 1113.partners[_ngcontent-ng-c3721682887] .logo.active[_ngcontent-ng-c3721682887] img[_ngcontent-ng-c3721682887], .partners.static[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887] img[_ngcontent-ng-c3721682887] { 1114 filter: invert(1) hue-rotate(180deg); 1115} 1116.partners.static[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887] { 1117 opacity: 1; 1118 pointer-events: none; 1119} 1120.partners[_ngcontent-ng-c3721682887] .panel[_ngcontent-ng-c3721682887] { 1121 grid-column: 1/-1; 1122 grid-row-start: calc(round(down, var(--i) / var(--cols), 1) + 2); 1123 display: grid; 1124 grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); 1125 gap: var(--spacing-3xl); 1126 padding-top: var(--spacing-xl); 1127 border-top: 1px solid var(--border-alt); 1128 transition: opacity var(--transition-fast), translate var(--transition-fast); 1129} 1130@starting-style { 1131 .partners[_ngcontent-ng-c3721682887] .panel[_ngcontent-ng-c3721682887] { 1132 opacity: 0; 1133 translate: 0 6px; 1134 } 1135} 1136.partners[_ngcontent-ng-c3721682887] .panel[hidden][_ngcontent-ng-c3721682887] { 1137 display: none; 1138} 1139.partners[_ngcontent-ng-c3721682887] .name[_ngcontent-ng-c3721682887] { 1140 font-size: var(--font-size-xl); 1141 font-weight: 600; 1142 line-height: 1.3; 1143 color: var(--on-surface); 1144} 1145.partners[_ngcontent-ng-c3721682887] .tagline[_ngcontent-ng-c3721682887] { 1146 margin-top: var(--spacing-2xs); 1147 font-family: var(--font-inter); 1148 font-size: var(--font-size-base); 1149 line-height: var(--line-height-body); 1150 color: var(--on-surface-container); 1151 text-wrap: pretty; 1152} 1153.partners[_ngcontent-ng-c3721682887] .description[_ngcontent-ng-c3721682887] { 1154 font-family: var(--font-inter); 1155 font-size: var(--font-size-md); 1156 line-height: var(--line-height-relaxed); 1157 color: var(--on-surface); 1158 text-wrap: pretty; 1159} 1160@media (prefers-color-scheme: light) { 1161 .partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887] img[_ngcontent-ng-c3721682887] { 1162 filter: grayscale(1); 1163 mix-blend-mode: multiply; 1164 } 1165 .partners[_ngcontent-ng-c3721682887] .logo.active[_ngcontent-ng-c3721682887] img[_ngcontent-ng-c3721682887], .partners.static[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887] img[_ngcontent-ng-c3721682887] { 1166 filter: none; 1167 } 1168} 1169@media (prefers-reduced-motion: reduce) { 1170 .partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887], 1171 .partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887]::after, 1172 .partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887] img[_ngcontent-ng-c3721682887], 1173 .partners[_ngcontent-ng-c3721682887] .panel[_ngcontent-ng-c3721682887] { 1174 transition: none; 1175 } 1176} 1177@media (max-width: 640px) { 1178 .partners[_ngcontent-ng-c3721682887] { 1179 --cols: 2; 1180 gap: var(--spacing-xl) var(--spacing-lg); 1181 } 1182 .partners[_ngcontent-ng-c3721682887] .logo[_ngcontent-ng-c3721682887] { 1183 height: 44px; 1184 } 1185 .partners[_ngcontent-ng-c3721682887] .panel[_ngcontent-ng-c3721682887] { 1186 grid-template-columns: minmax(0, 1fr); 1187 gap: var(--spacing-md); 1188 } 1189}</style>
1189<script id="ld-jsonld-page" type="application/ld+json">{"@context":"https://schema.org","@type":"SoftwareApplication","@id":"https://prettywhale.ai/#product","name":"PrettyWhale.ai","applicationCategory":"DeveloperApplication","softwareRequirements":"Visual Studio Code","url":"https://prettywhale.ai/","description":"Generates data pipelines from a sample of data, with its unit tests, data quality checks, schemas, orchestration scripts and documentation, ready to deploy in production.","featureList":["Data pipelines generated from a data sample","Unit tests","Data quality checks","Schema generation","Orchestration and deployment scripts","Documentation"],"publisher":{"@id":"https://prettywhale.ai/#organization"}}</script>
1189<style>@charset "UTF-8"; 1190[_nghost-ng-c2265933664] { 1191 display: block; 1192} 1193 1194.card[_ngcontent-ng-c2265933664] { 1195 display: flex; 1196 flex-direction: column; 1197 gap: var(--spacing-lg); 1198 height: 100%; 1199 padding: var(--spacing-lg); 1200 border-radius: var(--border-radius-xs); 1201 border: 1px solid var(--border-alt); 1202 background-color: var(--surface); 1203 overflow: hidden; 1204 transition: border-color var(--transition-fast); 1205} 1206 1207.badge[_ngcontent-ng-c2265933664] { 1208 display: inline-flex; 1209 align-items: center; 1210 align-self: flex-start; 1211 height: 24px; 1212 padding: 0 var(--spacing-xs); 1213 border: 1px solid transparent; 1214 border-radius: var(--border-radius-xs); 1215 font-family: var(--font-inter); 1216 font-weight: 500; 1217 font-size: var(--font-size-sm); 1218 line-height: 16px; 1219} 1220 1221 1222 1223.badge.neutral[_ngcontent-ng-c2265933664] { 1224 background-color: var(--surface-container); 1225 border-color: var(--border-alt); 1226 color: var(--on-surface); 1227} 1228 1229.body[_ngcontent-ng-c2265933664] { 1230 display: flex; 1231 flex-direction: column; 1232 gap: var(--spacing-xs); 1233} 1234 1235.card-title[_ngcontent-ng-c2265933664] { 1236 font-family: var(--font-heading); 1237 font-weight: 500; 1238 font-size: var(--font-size-base); 1239 line-height: 24px; 1240 color: var(--on-surface); 1241 overflow-wrap: break-word; 1242 transition: color var(--transition-fast); 1243 display: -webkit-box; 1244 -webkit-line-clamp: 2; 1245 line-clamp: 2; 1246 -webkit-box-orient: vertical; 1247 overflow: hidden; 1248} 1249 1250.card[_ngcontent-ng-c2265933664]:hover .card-title[_ngcontent-ng-c2265933664] { 1251 color: var(--primary); 1252} 1253 1254.card-meta[_ngcontent-ng-c2265933664] { 1255 display: flex; 1256 align-items: center; 1257 gap: var(--spacing-xs); 1258 font-family: var(--font-inter); 1259 font-weight: 400; 1260 font-size: var(--font-size-base); 1261 line-height: 24px; 1262 color: var(--on-surface-disable); 1263} 1264 1265.excerpt[_ngcontent-ng-c2265933664] { 1266 font-family: var(--font-inter); 1267 font-weight: 400; 1268 font-size: var(--font-size-base); 1269 line-height: 24px; 1270 color: var(--on-surface-container); 1271 display: -webkit-box; 1272 -webkit-line-clamp: 3; 1273 -webkit-box-orient: vertical; 1274 overflow: hidden; 1275} 1276 1277 1278 1279.explore[_ngcontent-ng-c2265933664] { 1280 display: inline-flex; 1281 align-items: center; 1282 align-self: flex-start; 1283 gap: var(--spacing-xs); 1284 margin-top: auto; 1285 font-family: var(--font-inter); 1286 font-weight: 500; 1287 font-size: var(--font-size-sm); 1288 line-height: 16px; 1289 color: var(--on-surface); 1290 transition: color var(--transition-fast); 1291} 1292 1293.card[_ngcontent-ng-c2265933664]:hover .explore[_ngcontent-ng-c2265933664] { 1294 color: var(--primary); 1295} 1296 1297.icon-arrow[_ngcontent-ng-c2265933664] { 1298 display: inline-block; 1299 width: 16px; 1300 height: 16px; 1301 background-color: currentColor; 1302 mask-image: url("/assets/icons/arrow-diagonal.svg"); 1303 mask-size: contain; 1304 mask-repeat: no-repeat; 1305} 1306 1307 1308 1309.size-large[_nghost-ng-c2265933664] .excerpt[_ngcontent-ng-c2265933664] { 1310 -webkit-line-clamp: 4; 1311} 1312 1313 1314 1315.size-small[_nghost-ng-c2265933664] .card[_ngcontent-ng-c2265933664] { 1316 gap: var(--spacing-xs); 1317 justify-content: center; 1318} 1319.size-small[_nghost-ng-c2265933664] .excerpt[_ngcontent-ng-c2265933664] { 1320 -webkit-line-clamp: 1; 1321}</style></head> 1322 1323<body><!--nghm--> 1324 <pw-root ng-version="21.2.5" ngh="8" ng-server-context="ssr-analog"><router-outlet></router-outlet><ng-component ngh="1"><pw-header _nghost-ng-c1321049901="" ngh="0"><header _ngcontent-ng-c1321049901="" class="header"><div _ngcontent-ng-c1321049901="" class="inner container"><a _ngcontent-ng-c1321049901="" routerlink="/" class="logo" href="/"><img _ngcontent-ng-c1321049901="" ngsrc="/assets/prettywhale_white.svg" alt="PrettyWhale logo" width="90" height="41" priority="" loading="eager" fetchpriority="high" decoding="sync" ng-img="true" src="/assets/prettywhale_white.svg"><span _ngcontent-ng-c1321049901="" class="logo-text"> PrettyWhale<span _ngcontent-ng-c1321049901="" class="logo-accent">.ai</span></span></a><nav _ngcontent-ng-c1321049901="" class="nav"><a _ngcontent-ng-c1321049901="" routerlink="/" class="link" href="/">Product</a><a _ngcontent-ng-c1321049901="" routerlink="/engineering-ai" class="link" href="/engineering-ai">Engineering AI</a><div _ngcontent-ng-c1321049901="" class="resources">
1324<button _ngcontent-ng-c1321049901="" type="button" aria-controls="resources-menu" class="link resources-toggle" aria-expanded="false"> Resources <span _ngcontent-ng-c1321049901="" aria-hidden="true" class="icon-chevron"></span></button><span _ngcontent-ng-c1321049901="" aria-hidden="true" class="resources-heading">Resources</span><ul _ngcontent-ng-c1321049901="" id="resources-menu" role="list" class="resources-menu"><li _ngcontent-ng-c1321049901=""><!----><a _ngcontent-ng-c1321049901="" class="link resources-link" href="/blog"> Blog </a><!----></li><li _ngcontent-ng-c1321049901=""><!----><a _ngcontent-ng-c1321049901="" class="link resources-link" href="/faq"> FAQ </a><!----></li><li _ngcontent-ng-c1321049901=""><!----><a _ngcontent-ng-c1321049901="" class="link resources-link" href="/career"> Careers </a><!----></li><li _ngcontent-ng-c1321049901=""><!----><a _ngcontent-ng-c1321049901="" class="link resources-link" href="/partners"> Partners </a><!----></li><li _ngcontent-ng-c1321049901=""><a _ngcontent-ng-c1321049901="" target="_blank" rel="noopener noreferrer" class="link resources-link" href="https://docs.prettywhale.ai/introduction">Docs</a><!----><!----></li><!----></ul></div><a _ngcontent-ng-c1321049901="" target="_blank" rel="noopener noreferrer" aria-label="Install for VS Code" class="install" href="https://marketplace.visualstudio.com/items?itemName=prettywhale-ai.prettywhale"><span _ngcontent-ng-c1321049901="" aria-hidden="true" class="icon-editor"></span> Install <span _ngcontent-ng-c1321049901="" aria-hidden="true" class="icon-arrow"></span></a><a _ngcontent-ng-c1321049901="" routerlink="/contact" class="contact" href="/contact">Ask for a demo</a></nav><button _ngcontent-ng-c1321049901="" aria-label="Toggle navigation menu" class="burger" aria-expanded="false"><span _ngcontent-ng-c1321049901="" class="burger-line"></span><span _ngcontent-ng-c1321049901="" class="burger-line"></span><span _ngcontent-ng-c1321049901="" class="burger-line"></span></button></div></header></pw-header><main><pw-hero _nghost-ng-c2863248315="" ngh="1"><section _ngcontent-ng-c2863248315="" class="hero"><pw-water-caustic _ngcontent-ng-c2863248315="" _nghost-ng-c1482773486="" ngh="1"><canvas _ngcontent-ng-c1482773486=""></canvas></pw-water-caustic><div _ngcontent-ng-c2863248315="" class="inner container"><div _ngcontent-ng-c2863248315="" class="content"><h1 _ngcontent-ng-c2863248315="" class="title"> Ingestion Code You <span _ngcontent-ng-c2863248315="" class="gradient">Trust</span></h1><p _ngcontent-ng-c2863248315="" class="subtitle"> Generate your data ingestion pipelines effortlessly.<br _ngcontent-ng-c2863248315=""></p><div _ngcontent-ng-c2863248315="" class="actions"><a _ngcontent-ng-c2863248315="" target="_blank" rel="noopener noreferrer" class="install-link" href="https://marketplace.visualstudio.com/items?itemName=prettywhale-ai.prettywhale"><span _ngcontent-ng-c2863248315="" aria-hidden="true" class="icon-editor"></span> Install for VS Code <span _ngcontent-ng-c2863248315="" aria-hidden="true" class="icon-arrow"></span></a><a _ngcontent-ng-c2863248315="" routerlink="/contact" class="contact-link" href="/contact">Contact us</a></div></div><div _ngcontent-ng-c2863248315="" class="visual"><img _ngcontent-ng-c2863248315="" ngsrc="assets/code.svg" alt="Code example" width="600" height="375" priority="" loading="eager" fetchpriority="high" decoding="sync" ng-img="true" src="assets/code.svg"></div></div></section></pw-hero><pw-demo-section _nghost-ng-c312145568="" ngh="1"><section _ngcontent-ng-c312145568="" class="demo-section"><div _ngcontent-ng-c312145568="" class="container"><div _ngcontent-ng-c312145568="" class="heading"><h2 _ngcontent-ng-c312145568="" class="title">We build an Engineering AI for data teams</h2><p _ngcontent-ng-c312145568="" class="subtitle"> PrettyWhale.ai is an Engineering AI built on a specialized small language model (SLM) trained for one job: <br _ngcontent-ng-c312145568=""><strong _ngcontent-ng-c312145568="">generate ingestion and integration code.</strong></p></div><div _ngcontent-ng-c312145568="" class="preview"><video _ngcontent-ng-c312145568="" src="assets/videos/prettywhale-teaser-dark.mp4" loop="" muted="" playsinline=""></video></div></div></section></pw-demo-section><pw-stepper _nghost-ng-c1779684384="" ngh="2"><section _ngcontent-ng-c1779684384="" class="stepper"><div _ngcontent-ng-c1779684384="" class="container"><div _ngcontent-ng-c1779684384="" class="header"><div _ngcontent-ng-c1779684384="" class="heading"><h2 _ngcontent-ng-c1779684384="" class="title">How it works</h2><p _ngcontent-ng-c1779684384="" class="subtitle"> From a sample of data to a complete pipeline, effortlessly. </p></div><div _ngcontent-ng-c1779684384="" class="nav"><button _ngcontent-ng-c1779684384="" aria-label="Scroll left" class="nav-btn disabled" disabled=""><span _ngcontent-ng-c1779684384="" aria-hidden="true" class="nav-icon" style="mask-image: url('/assets/icons/large-chevron-left.svg'); -webkit-mask-image: url('/assets/icons/large-chevron-left.svg');"></span></button><button _ngcontent-ng-c1779684384="" aria-label="Scroll right" class="nav-btn disabled" disabled=""><span _ngcontent-ng-c1779684384="" aria-hidden="true" class="nav-icon" style="mask-image: url('/assets/icons/large-chevron-right.svg'); -webkit-mask-image: url('/assets/icons/large-chevron-right.svg');"></span></button></div></div></div><div _ngcontent-ng-c1779684384="" class="track"><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: rgba(23, 40, 93, 1); --step-color: var(--const-white); --step-height: 333px; --step-hover-height: 361px; background-image: url(/assets/step-1.svg);"><div _ngcontent-ng-c1779684384="" class="step-badge">I</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Discovery</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Identification sample analysis</p><p _ngcontent-ng-c1779684384="" class="step-description">Import a sample file (JSON, CSV, etc.) so PrettyWhale.ai can automatically analyze its structure, detect fields, and understand how your data is organized. No manual setup required.</p></div></article><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: var(--primary); --step-color: var(--const-black); --step-height: 275px; --step-hover-height: 327px; background-image: url(/assets/step-2.svg);"><div _ngcontent-ng-c1779684384="" class="step-badge">II</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Selection</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Scope of data</p><p _ngcontent-ng-c1779684384="" class="step-description">PrettyWhale.ai generates a detailed analysis of your data, highlighting each field with examples and insights. You can then select only the fields you want to keep, giving you full control over your dataset.</p></div></article><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: var(--branding); --step-color: var(--const-black); --step-height: 304px; --step-hover-height: 395px; background-image: none;"><div _ngcontent-ng-c1779684384="" class="step-badge">III</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Processings</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Transform suggestion</p><p _ngcontent-ng-c1779684384="" class="step-description">Based on the analysis, PrettyWhale.ai suggests relevant transformations such as normalization, formatting, and data cleaning. You can easily review, adjust, remove, or add your own transformations to match your exact requirements.</p></div></article><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: var(--tertiary); --step-color: var(--const-white); --step-height: 340px; --step-hover-height: 437px; background-image: url(/assets/step-4.svg);"><div _ngcontent-ng-c1779684384="" class="step-badge">IV</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Enrichments</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Suggestion and selection</p><p _ngcontent-ng-c1779684384="" class="step-description">Enhance your dataset by connecting to external data sources. PrettyWhale.ai helps you enrich your data with additional context, making it more complete and valuable for downstream use.</p></div></article><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: var(--primary); --step-color: var(--const-black); --step-height: 333px; --step-hover-height: 361px; background-image: none;"><div _ngcontent-ng-c1779684384="" class="step-badge">V</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Output generation</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Code and deliverables</p><p _ngcontent-ng-c1779684384="" class="step-description">PrettyWhale.ai generates everything you need: ingestion code, documentation, schemas, and unit tests. The code is validated and ready to be deployed in production, saving hours of manual work.</p></div></article><!----></div></section></pw-stepper><pw-focus-tabs _nghost-ng-c2440753150="" ngh="3"><section _ngcontent-ng-c2440753150="" class="focus-tabs"><div _ngcontent-ng-c2440753150="" class="container"><h2 _ngcontent-ng-c2440753150="" class="title">Focus on what matters</h2><div _ngcontent-ng-c2440753150="" class="layout"><div _ngcontent-ng-c2440753150="" class="tabs"><div _ngcontent-ng-c2440753150="" class="tab-block"><button _ngcontent-ng-c2440753150="" class="tab active"><span _ngcontent-ng-c2440753150="" aria-hidden="true" class="tab-icon" style="mask-image: url(/assets/icons/debug-step-over.svg); -webkit-mask-image: url(/assets/icons/debug-step-over.svg);"></span><div _ngcontent-ng-c2440753150=""><span _ngcontent-ng-c2440753150="" class="tab-title">Skip the repetitive work</span><span _ngcontent-ng-c2440753150="" class="tab-description">Stop wasting time manually writing your data pipelines. Generate your ingestion scripts in minutes!</span></div></button><div _ngcontent-ng-c2440753150="" class="tab-panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="tab-image" src="/assets/focus-tab-1.png" alt="Skip the repetitive work"></div></div></div><div _ngcontent-ng-c2440753150="" class="tab-block"><button _ngcontent-ng-c2440753150="" class="tab"><span _ngcontent-ng-c2440753150="" aria-hidden="true" class="tab-icon" style="mask-image: url(/assets/icons/file-code.svg); -webkit-mask-image: url(/assets/icons/file-code.svg);"></span><div _ngcontent-ng-c2440753150=""><span _ngcontent-ng-c2440753150="" class="tab-title">Native data quality integration</span><span _ngcontent-ng-c2440753150="" class="tab-description">Writing validation code is time-consuming and tedious. PrettyWhale.ai takes care of it for you: it analyses your data sources and generates the necessary Data quality checks. </span></div></button><div _ngcontent-ng-c2440753150="" class="tab-panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="tab-image" src="/assets/focus-tab-2.png" alt="Native data quality integration"></div></div></div><div _ngcontent-ng-c2440753150="" class="tab-block"><button _ngcontent-ng-c2440753150="" class="tab"><span _ngcontent-ng-c2440753150="" aria-hidden="true" class="tab-icon" style="mask-image: url(/assets/icons/copy.svg); -webkit-mask-image: url(/assets/icons/copy.svg);"></span><div _ngcontent-ng-c2440753150=""><span _ngcontent-ng-c2440753150="" class="tab-title">Standardize code for maintenance</span><span _ngcontent-ng-c2440753150="" class="tab-description">
1324AI generates structured, well-documented code that complies with industry standards, making debugging and code maintenance much easier.</span></div></button><div _ngcontent-ng-c2440753150="" class="tab-panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="tab-image" src="/assets/focus-tab-3.png" alt="Standardize code for maintenance"></div></div></div><div _ngcontent-ng-c2440753150="" class="tab-block"><button _ngcontent-ng-c2440753150="" class="tab"><span _ngcontent-ng-c2440753150="" aria-hidden="true" class="tab-icon" style="mask-image: url(/assets/icons/verified.svg); -webkit-mask-image: url(/assets/icons/verified.svg);"></span><div _ngcontent-ng-c2440753150=""><span _ngcontent-ng-c2440753150="" class="tab-title">Production ready from day one</span><span _ngcontent-ng-c2440753150="" class="tab-description">Integrate unit tests, quality checks, and deployment and orchestration scripts directly into your generated code. Shorten the development cycle and deliver robust data flows free from beginnerâs mistakes.</span></div></button><div _ngcontent-ng-c2440753150="" class="tab-panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="tab-image" src="/assets/focus-tab-4.png" alt="Production ready from day one"></div></div></div><!----></div><div _ngcontent-ng-c2440753150="" class="panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="panel-image" src="/assets/focus-tab-1.png" alt="Skip the repetitive work"></div></div></div></div></section></pw-focus-tabs><pw-partners-preview _nghost-ng-c3308003094="" ngh="1"><section _ngcontent-ng-c3308003094="" class="partners-preview"><div _ngcontent-ng-c3308003094="" class="container"><div _ngcontent-ng-c3308003094="" class="header"><h2 _ngcontent-ng-c3308003094="" class="title">Our partners</h2><a _ngcontent-ng-c3308003094="" routerlink="/partners" class="link" href="/partners"> Meet our partners <span _ngcontent-ng-c3308003094="" aria-hidden="true" class="icon-arrow"></span></a></div><pw-partner-list _ngcontent-ng-c3308003094="" _nghost-ng-c3721682887="" ngh="4"><div _ngcontent-ng-c3721682887="" role="group" aria-label="Partners" class="partners static"><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="Eleven" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/eleven.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="Spoon Consulting" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/spoon-consulting.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="Moove-SI" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/moove-si.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="JEMS" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/jems.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="D&Ã" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/d-and-u.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><!----></div></pw-partner-list></div></section></pw-partners-preview><pw-blog-preview _nghost-ng-c4283817964="" ngh="6"><section _ngcontent-ng-c4283817964="" class="blog-preview"><div _ngcontent-ng-c4283817964="" class="container"><div _ngcontent-ng-c4283817964="" class="header"><h2 _ngcontent-ng-c4283817964="" class="title">PrettyWhale.ai blog</h2><a _ngcontent-ng-c4283817964="" routerlink="/blog" class="link" href="/blog"> Explore Articles <span _ngcontent-ng-c4283817964="" aria-hidden="true" class="icon-arrow"></span></a></div><div _ngcontent-ng-c4283817964="" class="grid"><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/the-great-ai-disruption-in-consulting"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">The Great AI Disruption in Consulting</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">6 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Sep 16, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Discover how AI is dismantling the billable hour in IT services. Explore how the shift toward outcome-guaranteed contracts and asset-based consulting is redefining professional services.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/the-age-of-generic-copilots-is-over-enter-specialized-harnesses"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">The Age of Generic Copilots Is Over. Enter Specialized Harnesses.</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">4 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Sep 3, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Code generation became a commodity. What did not is the engineering frame around the model. Why the next wave is specialized harnesses, and what one contains for data.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/data-engineering-doesnt-need-a-smarter-ai-copilot"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A;
1324 color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Data Engineering Doesn't Need a Smarter AI Copilot</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">5 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Sep 1, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Every correction you type into a copilot is a requirement you already knew. Driving one means re-expressing your standards forever, one prompt at a time.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/data-pipeline-vendor-lock-in-is-built-one-connector-at-a-time"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A; color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Data Pipeline Vendor Lock-In Is Built One Connector at a Time</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">5 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 26, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Lock-in is not a contract term, it is a rewrite estimate. Why the ingestion layer accumulates it faster than anything else, and how to measure yours today.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/small-language-models-for-code-generation-when-narrow-beats-large"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Small Language Models for Code Generation: When Narrow Beats Large</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">6 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 20, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Small language models can match large ones on a bounded job like code generation, at 10 to 30 times lower serving cost. Here is when that trade holds.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/who-accepts-code-nobody-wrote"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Who Accepts AI-Generated Code That Nobody Wrote</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">4 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 17, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Code review assumes an author who can answer for the code. Generated code breaks that assumption. What changes in the pull request, and after an incident.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/what-production-ready-means-for-a-data-pipeline-and-who-decides"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A; color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Production-Ready Data Pipelines: What It Means and Who Decides</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">5 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 12, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Ask two engineers when an ingestion pipeline is finished and you get two answers. Here is how to write the bar down on one page and enforce it in review.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/estimating-data-ingestion-work-a-unit-of-work-method"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A;
1324 color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Estimating Data Ingestion Work: A Unit-of-Work Method</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">7 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 10, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Stop estimating ingestion tasks one by one. Estimate units of work, build a tier grid from your own delivery history, and recalibrate it after every project.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/moove-si-and-prettywhale-ai-team-up"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #17285D; border-color: #17285D; color: #fafafa;">Partners</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Moove-SI and PrettyWhale.ai Team Up on Data & AI</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">2 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 6, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Moove-SI, an integrator specialising in Microsoft solutions, is adding PrettyWhale.ai to its Data & AI offer to speed up the costliest part of data projects.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/engineering-ai-copilots-and-code-generators-a-taxonomy"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Engineering AI, Copilots and Code Generators: A Taxonomy</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">6 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 5, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">"AI writes code now" covers four product families with four failure modes. The criterion that separates them, and four questions to pick the right one.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/the-end-of-the-it-services-company-model-based-solely-on-daily-rates"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A; color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">The End of the Daily-Rate Model for IT Services</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">2 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Jun 17, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">For thirty years IT services firms sold human time by the day. Clients now ask what you save them, not how many consultants you can put on the project.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/ingestion-vs-integration-code"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A; color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Ingestion vs Integration: A Confusion That Costs Millions</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">2 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">â¢</span><!----><time _ngcontent-ng-c2265933664="" class="date">Jun 12, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Ingestion and integration code get used as synonyms. They solve different problems, and the teams that optimise the wrong one pay for it in production.</p></div><!----></a></pw-post-card><!----></div></div></section></pw-blog-preview><pw-get-started _nghost-ng-c4078681124="" data-url="https://prettywhale.notion.site/Getting-started-334fb7946c3d80949cbbc49cc275f670" ngh="1"><section _ngcontent-ng-c4078681124="" class="get-started"><div _ngcontent-ng-c4078681124="" class="container"><h2 _ngcontent-ng-c4078681124="" class="title">Start building production-ready pipelines today</h2></div></section></pw-get-started></main><pw-footer _nghost-ng-c1940239115="" ngh="7"><footer _ngcontent-ng-c1940239115="" class="footer"><div _ngcontent-ng-c1940239115="" class="container"><nav _ngcontent-ng-c1940239115="" class="links"><div _ngcontent-ng-c1940239115="" class="col"><h4 _ngcontent-ng-c1940239115="" class="col-title">Company</h4><a _ngcontent-ng-c1940239115="" routerlink="/engineering-ai" class="link" href="/engineering-ai">Engineering AI</a><a _ngcontent-ng-c1940239115="" routerlink="/contact" class="link" href="/contact">Contact</a></div><div _ngcontent-ng-c1940239115="" class="col"><h4 _ngcontent-ng-c1940239115="" class="col-title">Resources</h4><!----><a _ngcontent-ng-c1940239115="" class="link" href="/blog">Blog</a><!----><!----><a _ngcontent-ng-c1940239115="" class="link" href="/faq">FAQ</a><!----><!----><a _ngcontent-ng-c1940239115="" class="link" href="/career">Careers</a><!----><!----><a _ngcontent-ng-c1940239115="" class="link" href="/partners">Partners</a><!----><a _ngcontent-ng-c1940239115="" target="_blank" rel="noopener noreferrer" class="link" href="https://docs.prettywhale.ai/introduction"> Docs </a><!----><!----><!----></div><div _ngcontent-ng-c1940239115="" class="col"><h4 _ngcontent-ng-c1940239115="" class="col-title">Legal</h4><a _ngcontent-ng-c1940239115="" routerlink="/legal-notice" class="link" href="/legal-notice">Legal notice</a></div>
1324<a _ngcontent-ng-c1940239115="" href="https://www.linkedin.com/company/prettywhale-ai/" target="_blank" rel="noopener noreferrer" aria-label="PrettyWhale on LinkedIn" class="social-link"><span _ngcontent-ng-c1940239115="" class="icon linkedin"></span></a></nav><p _ngcontent-ng-c1940239115="" class="copyright"> © 2026 Copyright PrettyWhale.ai - Question about data privacy ? Send an email to <a _ngcontent-ng-c1940239115="" href="mailto:[email protected]" class="email">[email protected]</a></p></div></footer></pw-footer></ng-component><!----></pw-root> 1325 1326 <!-- LinkedIn Insight Tag --> 1327
1327<script type="text/javascript"> 1328 if (window.location.hostname === "prettywhale.ai") { 1329 _linkedin_partner_id = "10836025"; 1330 window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || []; 1331 window._linkedin_data_partner_ids.push(_linkedin_partner_id); 1332 (function (l) { 1333 if (!l) { 1334 window.lintrk = function (a, b) { 1335 window.lintrk.q.push([a, b]); 1336 }; 1337 window.lintrk.q = []; 1338 } 1339 var s = document.getElementsByTagName("script")[0]; 1340 var b = document.createElement("script"); 1341 b.type = "text/javascript"; 1342 b.async = true; 1343 b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js"; 1344 s.parentNode.insertBefore(b, s); 1345 })(window.lintrk); 1346 } 1347 </script>
1347 1348 <noscript> 1349 <img height="1" width="1" style="display: none" alt="" src="https://px.ads.linkedin.com/collect/?pid=10836025&fmt=gif"> 1350 </noscript> 1351 1352 1353 1354
1355<script id="ng-state" type="application/json">{"analog_444896916":{"body":[{"title":"The Great AI Disruption in Consulting","slug":"the-great-ai-disruption-in-consulting","excerpt":"Discover how AI is dismantling the billable hour in IT services. Explore how the shift toward outcome-guaranteed contracts and asset-based consulting is redefining professional services.","html":"\u003Ch2 id=\"the-collapse-of-the-legacy-consulting-equation\">The Collapse of the Legacy Consulting Equation\u003C/h2>\u003Cp>For decades, the global professional services industry, management consultancies, and IT service providers built their financial engines around a single commercial equation: \u003Cstrong>Revenue = Billable Hours à Rate Cards\u003C/strong>. In this input-driven framework, firm profitability scaled proportionately with human headcount and engagement duration. The economic incentives were simple: the more consultants deployed and the longer a project took, the higher the top-line revenue.\u003C/p>\u003Cp>The rapid maturity of artificial intelligence (specifically generative algorithms, agentic coding tools, and automated workflow orchestrators) has fundamentally shattered this equation. By automating research, data synthesis, software development, and process modeling, AI compresses tasks that previously required weeks of human labor into minutes or hours.\u003C/p>\u003Cp>This acceleration forces professional services into a critical juncture. If a firm continues to bill clients on a Time and Materials (T&M), using AI to increase delivery speed directly destroys its own top-line revenue. To avoid this structural trap, the consulting sector is undergoing its most radical transformation in modern history.\u003C/p>\u003Cp>By synthesizing macroeconomic data, strategic research, and M&A trends, this state-of-the-market report explores how market leaders are abandoning legacy rate cards, establishing outcome-guaranteed commercial models, and acquiring software assets to decouple revenue growth from headcount expansion.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"1-the-economic-collapse-of-input-based-consulting\">1. The Economic Collapse of Input-Based Consulting\u003C/h2>\u003Cp>The immediate reflex of most enterprise leaders when adopting a breakthrough technology is to seek task-level efficiencyâperforming existing work faster, cheaper, and with fewer resources. However, applying AI on top of legacy processes creates a severe market dynamic known as the \u003Cstrong>\"productivity trap\"\u003C/strong>.\u003C/p>\u003Cp>When general-purpose AI capabilities become widely accessible across the market, basic efficiency gains cease to function as a competitive differentiator. As competing service providers adopt the same automated tools, delivery speed is commoditized, and the resulting financial savings are rapidly passed downstream to clients or competed away entirely.\u003C/p>\u003Cp>This commoditization creates an immediate operational liability for input-based business models. Under traditional T&M contracts, the client absorbs the financial risk of inefficiency while the provider is financially rewarded for extended project timelines. AI completely inverts this dynamic:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Procurement Backlash:\u003C/strong> Enterprise buyers and CFOs are acutely aware that consulting teams utilize AI code assistants, automated research tools, and prompt frameworks. Procurement teams increasingly reject standard hourly billing for work generated in seconds by algorithms.\u003Cbr>\u003C/li>\u003Cli>\u003Cstrong>Revenue Compression:\u003C/strong> When a 40-hour research or modeling deliverable is completed in 4 hours using AI, a consultancy billing on time and materials loses 90% of its contract value despite delivering a faster, higher-quality result.\u003Cbr>\u003C/li>\u003Cli>\u003Cstrong>Misaligned Execution Risk:\u003C/strong> In an AI-augmented environment, continuing to charge by the hour incentivizes firms to delay automation adoption internally to protect billing volume, creating friction between provider incentives and client expectations.\u003Cbr>\u003C/li>\u003C/ul>\u003Cp>As a result, time-and-materials billing is transitioning from a industry standard into a structural disadvantage. Sustainable profitability no longer comes from selling human labor by the hour, but from productizing intellectual property, redesigning end-to-e
1355nd workflows, and capturing the value created by continuous learning operating models.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"2-re-architecting-contracts-from-hours-billed-to-outcome-guarantees\">2. Re-Architecting Contracts: From Hours Billed to Outcome Guarantees\u003C/h2>\u003Cp>To escape the productivity trap, forward-thinking service providers are overhauling their commercial terms. Instead of selling team capacity or analyst hours, consultancies are transitioning toward \u003Cstrong>Outcome-Based Consulting\u003C/strong> and value-based pricing models.\u003C/p>\u003Cp>In an outcome-guaranteed framework, contract value is tied directly to verifiable business metrics rather than activity inputs. Rather than billing $250,000 for 1,000 hours of supply chain analysis, a firm contracts for a guaranteed 15% reduction in inventory holding costs or a specific improvement in order-fulfillment cycles.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Cstrong>Commercial Dimension\u003C/strong>\u003C/th>\n\u003Cth>\u003Cstrong>Legacy Input-Based Model (T&M)\u003C/strong>\u003C/th>\n\u003Cth>\u003Cstrong>Modern Value-Based Model (Outcome-Guaranteed)\u003C/strong>\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Primary Pricing Metric\u003C/strong>\u003C/td>\n\u003Ctd>Hours, days, and staff rates.\u003C/td>\n\u003Ctd>Business milestones, value-share, usage tiers.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Financial Risk\u003C/strong>\u003C/td>\n\u003Ctd>Carried almost entirely by the client.\u003C/td>\n\u003Ctd>Shared between client and service provider.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Provider Incentive\u003C/strong>\u003C/td>\n\u003Ctd>Maximize hours worked per engagement.\u003C/td>\n\u003Ctd>Deploy AI tools to achieve outcomes as fast as possible.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Delivery Mechanism\u003C/strong>\u003C/td>\n\u003Ctd>Bespoke human labor per engagement.\u003C/td>\n\u003Ctd>Productized AI workflows, domain data, and IP assets.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Scalability\u003C/strong>\u003C/td>\n\u003Ctd>Tied directly to employee headcount growth.\u003C/td>\n\u003Ctd>Decoupled from headcount via software platforms.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>This contractual shift fundamentally aligns the goals of the client and the provider. Under an outcome guarantee, the service provider is fully incentivized to deploy proprietary AI models, pre-built workflow automations, and agentic tools as aggressively as possible. The faster and more efficiently the provider achieves the contracted result, the higher its internal profit margin.\u003C/p>\u003Cp>Furthermore, this shift changes how enterprise IT programs measure success. Instead of tracking vanity metrics like \"hours eliminated\" or \"documents processed per analyst,\" leadership teams evaluate providers based on end-to-end friction reduction, cost-to-serve optimizations, and cycle-time compressions.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"3-the-asset-arbitrage-scaling-revenue-without-scaling-headcount\">3. The Asset Arbitrage: Scaling Revenue Without Scaling Headcount\u003C/h2>\u003Cp>Transitioning to outcome-guaranteed contracts requires service providers to possess reusable intellectual property, automated platforms, and pre-trained industry models. Because building these assets organically takes time, global IT service giants are pursuing aggressive M&A strategies to buy their way into software-led models.\u003C/p>\u003Cp>Market research tracking M&A activity across 24 leading global service providers reveals a massive structural pivot toward \u003Cstrong>Asset-Based Consulting (ABC)\u003C/strong>:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Product-Driven Acquisitions:\u003C/strong> Out of 97 major acquisitions analyzed across top service firms, nearly \u003Cstrong>1 in 5 deals (20%)\u003C/strong> involved software products, digital platforms, or hybrid service-product solutions. Two-thirds of these technology deals were executed specifically to acquire the underlying software asset rather than talent or agency capacity.\u003Cbr>\u003C/li>\u003Cli>\u003Cstrong>The $9 Billion Non-FTE Mandate:\u003C/strong> Market leaders like Accenture have allocated up to \u003Cstrong>$9 billion\u003C/strong> in acquisition capital specifically to expand into \u003Cstrong>non-FTE (Full-Time Equivalent) commercial models\u003C/strong>. The strategic goal is to increase top-line market share without requiring a matching surge in employee headcount.\u003Cbr>\u003C/li>\u003Cli>\u003Cstrong>Hybrid Monetization Streams:\u003C/strong> By acquiring proprietary software platforms (ranging from cybersecurity orchestrators to industry-specific data engines), service providers can blend advisory fees with recurring SaaS subscriptions, platform usage rates, and performance bonuses.\u003Cbr>\u003C/li>\u003C/ul>\u003Cp>This M&A wave represents an industry evolution from labor arbitrage to \u003Cstrong>asset and IP arbitrage\u003C/strong>. Instead of starting from scratch on every client engagement, modern consultancies arrive with pre-configured AI software assets that perform 70% of the heavy lifting out of the box. Human consultants then focus on high-value strategic positioning, organizational change management, and system integration.\u003C/p>\u003Cp> \u003C/p>
1355\n\u003Ch2 id=\"4-strategic-playbook-for-enterprise-buyers-and-service-leaders\">4. Strategic Playbook for Enterprise Buyers and Service Leaders\u003C/h2>\u003Cp>The convergence of outcome-based pricing, software asset acquisitions, and AI-driven automation requires a revised operating playbook for both corporate IT buyers and service provider executives.\u003C/p>\u003Ch3 id=\"for-enterprise-buyers-cios-and-procurement-teams\">For Enterprise Buyers, CIOs, and Procurement Teams\u003C/h3>\u003Col>\u003Cli>\u003Cstrong>Eliminate Blended Rate Cards for AI Projects:\u003C/strong> Stop signing open-ended Time & Materials contracts for AI transformation projects. Require suppliers to structure Statements of Work (SOWs) around fixed business outcomes, transaction-based pricing, or shared-risk gainshare models.\u003C/li>\u003Cli>\u003Cstrong>Contract for Reusable IP Deliverables:\u003C/strong> When a service provider utilizes custom prompt libraries, analytical playbooks, or automated workflows during an engagement, explicitly define these artifacts as client-owned deliverables to enable long-term internal reuse.\u003C/li>\u003Cli>\u003Cstrong>Audit for Vendor Lock-In:\u003C/strong> As major service providers acquire independent software platforms, audit vendor choices during annual reviews to ensure that acquired software tools remain open, interoperable, and aligned with your broader enterprise architecture.\u003C/li>\u003C/ol>\u003Cp> \u003C/p>\n\u003Ch3 id=\"for-consultancies-and-service-executives\">For Consultancies, and Service Executives\u003C/h3>\u003Col>\u003Cli>\u003Cstrong>Productize Internal Frameworks:\u003C/strong> Audit internal delivery practices and convert bespoke methodologies, templates, and domain expertise into packaged, software-like assets and automated AI workflows.\u003C/li>\u003Cli>\u003Cstrong>Build Non-FTE Revenue Streams:\u003C/strong> Complement human advisory services with asset-backed recurring revenue, such as managed platform fees, continuous AI model maintenance, or subscription-based analytics.\u003C/li>\u003Cli>\u003Cstrong>Establish Clear Outcome Benchmarks:\u003C/strong> Develop precise, auditable baseline metrics (e.g., claims processing time, code deployment velocity, customer acquisition costs) so that value-based and outcome-guaranteed pricing contracts can be executed without contractual dispute.\u003C/li>\u003C/ol>\u003Cp> \u003C/p>\n\u003Ch2 id=\"conclusion-the-era-of-software-led-services\">Conclusion: The Era of Software-Led Services\u003C/h2>\u003Cp>The professional services and IT consulting industries are experiencing an irreversible structural shift. The era when firm growth depended purely on recruiting armies of junior analysts to sell billable hours is coming to a close.\u003C/p>\u003Cp>As market data demonstrates, attempting to rely on basic task efficiency traps service providers in margin compression. Meanwhile, enterprise buyers are rejecting input-based rate cards in favor of outcome-guaranteed engagements, and service giants are spending billions to acquire software assets that decouple revenue from headcount.\u003C/p>\u003Cp>In this new landscape, the most competitive IT service providers and consultancies will not be those with the largest headcount, but those that successfully combine proprietary software assets, outcome-based accountability, and AI-powered learning operating models to deliver guaranteed business value at unprecedented speed.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch3 id=\"sources-references\">Sources & References\u003C/h3>\u003Cul>\u003Cli>\u003Cstrong>McKinsey & Company:\u003C/strong> \u003Cem>The Real AI Advantage\u003C/em> (McKinsey Global Institute Research & Podcast featuring Tanguy Catlin, Roberta Fusaro, and Lucia Rahilly).\u003C/li>\u003Cli>\u003Cstrong>Cognitute:\u003C/strong> \u003Cem>Outcome-Based Consulting: Why Time and Materials Models Are Obsolete\u003C/em> (Cognitute Insights Framework).\u003C/li>\u003Cli>\u003Cstrong>Forrester Research:\u003C/strong> \u003Cem>Service Providers Are Acquiring Their Way To Relevance And Survival In An AI-Powered World\u003C/em>
1355 (Ted Schadler, VP and Principal Analyst).\u003C/li>\u003Cli>\u003Cstrong>MarketScale Business Services:\u003C/strong> \u003Cem>AI Is Pushing Consulting Away From Billable Hours and Toward Productized Learning Operating Models\u003C/em>.\u003C/li>\u003C/ul>\u003Cp> \u003C/p>\n\u003Chr>\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-09-16T10:50:32.000+00:00","reading_time":6,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"The Age of Generic Copilots Is Over. Enter Specialized Harnesses.","slug":"the-age-of-generic-copilots-is-over-enter-specialized-harnesses","excerpt":"Code generation became a commodity. What did not is the engineering frame around the model. Why the next wave is specialized harnesses, and what one contains for data.","html":"\u003Cp>For the past three years, the software industry has been obsessed with a single interface: the chat box. We were told that a large enough model, paired with a generic autocomplete plugin, would transform software engineering forever.\u003C/p>\u003Cp>It worked-up to a point: generating syntax, scaffolding boilerplates, or writing basic utility functions became trivial. But as the hype settles, engineering teams are facing a harsh reality: code generation is a solved problem; code verification and maintainability are the new issues.\u003C/p>\u003Cp>In specialized domains like Data Engineering, generic LLM copilots have hit a hard ceiling. The issue isn't the size of the context window or the reasoning capabilities of the underlying model. The issue is that generic tools lack an engineering frame.\u003C/p>\u003Cp>It is time to move past generic AI assistants. The next phase of engineering software belongs to specialized execution harnesses.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"from-models-to-frameworks-an-industry-pattern\">From Models to Frameworks: An Industry Pattern\u003C/h2>\u003Cp>To understand where AI-assisted engineering is going, we only need to look at the history of software development.\u003C/p>\u003Cp>In the early days of web development, engineers wrote raw scripts directly against web servers. It was flexible, fast to deploy, and completely unmanageable at scale. Security vulnerabilities were rampant, folder structures were arbitrary, and maintaining someone elseâs code was a nightmare.\u003C/p>\u003Cp>The industry didn't solve this by telling developers to \"think harder\" or write better comments. It solved
1355it through frameworks. These systems didn't take away the developer's agency; they imposed strict conventions, automated testing setups, defined migration pathways, and standardized execution environments.\u003C/p>\u003Cp>Generative AI is undergoing the exact same transition. Prompting a raw LLM through a chat sidebar is the modern equivalent of writing unconstrained web scripts in 2002. It gives you instant code, but leaves you with all the architectural debt.\u003C/p>\u003Cp>Data Engineering doesn't just need a model that knows better Python syntax. It needs an execution framework that understands the physics of data.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"why-generic-copilots-fail-at-data-integration\">Why Generic Copilots Fail at Data Integration\u003C/h2>\u003Cp>Web interfaces fail noisily with a broken component or a 500 status code. Data pipelines fail quietly.\u003C/p>\u003Cp>When an ingestion pipeline breaks due to a hidden API payload change, a missing null check, or an implicit type conversion, the pipeline rarely crashes immediately. Instead, it pollutes downstream data warehouses, corrupts analytical dashboards, and quietly wrecks machine learning models. You only discover the issue weeks later when financial reports don't reconcile.\u003C/p>\u003Cp>A generic AI copilot cannot prevent this because it operates without operational context:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>It guesses instead of profiling:\u003C/strong> A copilot looks at a small snippet of JSON and assumes the schema. It doesn't analyze 100,000 rows to detect edge-case nullability, varying array lengths, or timestamp variations.\u003C/li>\u003Cli>\u003Cstrong>It lacks deterministic guardrails:\u003C/strong> Prompting an LLM to \"handle all errors and follow best practices\" is a suggestion, not a guarantee. The model will still omit retry logic or drop malformed records when context limits pressure it.\u003C/li>\u003Cli>\u003Cstrong>It shifts the burden of proof:\u003C/strong> When a copilot generates 200 lines of complex extraction code, the data engineer must manually audit every single line. The time saved writing code is lost reviewing hallucinated logic.\u003C/li>\u003C/ul>\u003Cp>Asking a generic LLM to write data pipelines without strict environmental constraints is simply asking for technical debt.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"what-is-a-specialized-execution-harness\">What Is a Specialized Execution Harness?\u003C/h2>\u003Cp>A \u003Cstrong>Specialized Execution Harness\u003C/strong> is not a wrapper around an API, nor is it a complex system prompt. It is a deterministic execution environment built around a domain-specific model. It controls, constrains, validates, and tests every output produced by the AI before a human ever sees it.\u003C/p>\u003Cp>At \u003Ca href=\"https://prettywhale.ai/\" rel=\"noopener nofollow\">PrettyWhale.ai\u003C/a>, we designed our harness around the specific constraints of data ingestion and integration. Instead of relying on a multi-purpose LLM, the architecture couples a Small Language Model (SLM), trained explicitly on data engineering patterns, with a multi-stage validation engine.\u003C/p>\u003Cfigure class=\"kg-card kg-image-card\">\u003Cimg src=\"https://prettywhale.ai/content/images/2026/09/Pipeline-Flow-Diagram-selection-1.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1280\" height=\"1068\" srcset=\"https://prettywhale.ai/content/images/size/w600/2026/09/Pipeline-Flow-Diagram-selection-1.png 600w, https://prettywhale.ai/content/images/size/w1000/2026/09/Pipeline-Flow-Diagram-selection-1.png 1000w, https://prettywhale.ai/content/images/2026/09/Pipeline-Flow-Diagram-selection-1.png 1280w\" sizes=\"(min-width: 720px) 720px\">\u003C/figure>\u003Cp> \u003C/p>\n\u003Ch2 id=\"the-4-pillars-of-a-data-engineering-harness\">The 4 Pillars of a Data Engineering Harness\u003C/h2>\u003Col>\u003Cli>\u003Cstrong>Deterministic Analysis First:\u003C/strong> Before any code is generated, the harness runs static and dynamic analysis on raw source payloads (JSON, CSV, Parquet, or API endpoints). It calculates schema shapes, field cardinality, and boundary conditions programmatically.\u003C/li>\u003Cli>\u003Cstrong>Context Enforcement:\u003C/strong> The harness forces the generation process to adhere strictly to target destination specifications, corporate retry policies, logging standards, and schema validation rules.\u003C/li>\u003Cli>\u003Cstrong>Automated Sandbox Execution:\u003C/strong> The harness does not present raw code to the engineer. It spins up an isolated sandbox, executes the generated pipeline against real data samples, and verifies that data lands correctly without silent loss.\u003C/li>\u003Cli>\u003Cstrong>Self-Correction Loop:\u003C/strong> If the sandbox execution fails or detects a schema mismatch, the execution trace is fed directly back to the model. The system fixes its own code inside the harness before delivering the final result.\u003C/li>\u003C/ol>\u003Cp> \u003C/p>\n\u003Ch2 id=\"from-code-generation-to-software-engineering-ai\">From \"Code Generation\" to \"Software Engineering AI\"\u003C/h2>\u003Cp>
1355The fundamental shift happening right now is from \u003Cstrong>unassisted code generation\u003C/strong> to \u003Cstrong>verified software assets\u003C/strong>.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Cstrong>Generic Copilot Approach\u003C/strong>\u003C/th>\n\u003Cth>\u003Cstrong>Specialized Harness Approach\u003C/strong>\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Input:\u003C/strong> Natural language prompt (\u003Cem>\"Write a connector for X\"\u003C/em>).\u003C/td>\n\u003Ctd>\u003Cstrong>Input:\u003C/strong> Actual data source sample and schema constraints.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Process:\u003C/strong> Probabilistic completion based on web training data.\u003C/td>\n\u003Ctd>\u003Cstrong>Process:\u003C/strong> Profiling \u003Cspan class=\"math math-inline\">\\rightarrow\u003C/span> Specialized Generation \u003Cspan class=\"math math-inline\">\\rightarrow\u003C/span> Sandbox Execution.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Output:\u003C/strong> Raw text file containing code.\u003C/td>\n\u003Ctd>\u003Cstrong>Output:\u003C/strong> Verified package (Code, passing tests, schema, docs).\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Review Method:\u003C/strong> Manual line-by-line code audit by an engineer.\u003C/td>\n\u003Ctd>\u003Cstrong>Review Method:\u003C/strong> Verification of execution proofs and test results.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Failure Mode:\u003C/strong> Silent runtime failures in production.\u003C/td>\n\u003Ctd>\u003Cstrong>Failure Mode:\u003C/strong> Sandbox flags errors before deployment.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>When an AI system delivers code alongside its own execution proof, the developer's role evolves. You no longer spend 45 minutes line-debugging synthetic code. You spend two minutes reviewing verified test runs and architectural alignment.\u003C/p>\u003Cp>This is what turning AI into an engineering tool actually looks like.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"the-future-belongs-to-domain-specificity\">The Future Belongs to Domain Specificity\u003C/h2>\u003Cp>The era of expecting a single chat model to design user interfaces, optimize SQL queries, configure Kubernetes clusters, and write data ingestion pipelines with equal reliability is over.\u003C/p>\u003Cp>Just as general-purpose CPUs gave rise to specialized GPUs for graphics and AI workloads, general-purpose LLMs are giving way to specialized harnesses for domain-critical engineering tasks.\u003C/p>\u003Cp>At PrettyWhale.ai, we built our execution harness because we believe data engineers shouldn't spend their lives writing or debugging fragile boilerplate ingestion scripts. By wrapping domain-trained models in a deterministic, self-verifying harness, we can finally give data teams what they actually need: pipelines that work, backed by proof, every single time.\u003C/p>\u003Cp> \u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai is an Engineering AI for ingestion and integration code: a specialised harness around a small model trained on that code alone. It works from a real sample of the source and hands back a deliverable that has already run on that sample and passed its tests.\u003C/em>\u003C/p>\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-09-03T08:39:32.000+00:00","reading_time":4,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"Data Engineering Doesn't Need a Smarter AI Copilot","slug":"data-engineering-doesnt-need-a-smarter-ai-copilot","excerpt":"Every correction you type into a copilot is a requirement you already knew. Driving one means re-expressing your standards forever, one prompt at a time.","html":"\u003Cp>Ask an AI copilot for a CSV reader and you get one in four seconds. It is competent. Then you start typing.\u003C/p>\u003Cul>\u003Cli>Pin the encoding, do not rely on the locale.\u003C/li>\u003Cli>The malformed input test should assert the behaviour, not just that it did not crash.\u003C/li>\u003Cli>Externalise the file path and the thresholds.\u003C/li>\u003Cli>Keep the original value when the conversion fails.\u003C/li>\u003Cli>Add a schema, and validate against it in CI.\u003C/li>\u003Cli>Failures per record, not one status for the run.\u003C/li>\u003C/ul>\u003Cp>Six corrections, all reasonable but all you knew before you asked. A few hours later, you have a good pipeline.\u003C/p>\u003Cp>Tomorrow there is another source, and you start again.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"the-specification-exists-in-your-chat-history\">The specification exists in your chat history\u003C/h2>\u003Cp>That list is not feedback, it is your definition of done, expressed one prompt at a time, in a conversation that gets thrown away when the tab closes.\u003C/p>\u003Cp>This is the part that goes unnoticed, because each individual correction is cheap. A few hours is nothing. But you pay it per source, per project, and per person, and the sixth engineer on your team pays it without knowing the other five already did, because there is nowhere for that knowledge to live.\u003C/p>\u003Cp>Driving a copilot well means becoming very good at re-expressing your own standards. That is a real skill and it is a strange thing to get good at, because the value it produces evaporates every time.\u003C/p>\u003Cp>Here is a cheap measurement. Over your next week, keep a note of every instruction you give a generation tool that you have given it before. Not the ones about this specific source, the ones about how your team builds things. Then read the list.\u003C/p>\u003Cp>That list is a specification. It is not written down anywhere, and you are paying rent on it.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"a-more-capable-model-does-not-fix-this\">A more capable model does not fix this\u003C/h2>\u003Cp>The intuitive answer is that this is a transitional problem. Models get better, first drafts get closer, corrections shrink toward zero.\u003C/p>\u003Cp>Half of that is true. Capability reduces the corrections about correctness: the logic errors, the misunderstood requirement, the function that does not compile. Those genuinely go away as models improve, and they have been going away.\u003C/p>\u003Cp>It does nothing for the other half, and the other half is most of your list.\u003C/p>\u003Cp>Pinning the encoding is not a matter of intelligence. Neither is asserting behaviour in the malformed case, or externalising configuration, or preserving the original value. These are conventions. They are decisions your team made, or should have made, and they are not deducible from the request because they are not properties of the world. They are properties of you.\u003C/p>\u003Cp>A model cannot infer your bar. It can only be told, and if the telling happens in a conversation, it has to happen again in the next conversation. No amount of capability closes that loop, because the loop is not about capability. It is about where the specification lives.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"you-are-part-of-the-harness\">You are part of the harness\u003C/h2>\u003Cp>There is a shift worth borrowing in how people talk about these tools. The interesting part is no longer the model, it is the harness around it: the loop that calls tools, manages context, retries what failed, and decides when the work is done. Two products built on the same weights behave completely differently, and the harness is the difference.\u003C/p>\u003Cp>That framing is right, and it is worth pushing one step further. Ask what a general-purpose harness is able to verify.\u003C/p>\u003Cp>It can check the things that are true of all code. It compiles. The tests it wrote pass. The linter agrees. The types check. That is a genuinely useful set, and it is also the complete set, because a harness built to produce anything cannot assume anything about what finished looks like.\u003C/p>\u003Cp>Your conventions are not in that set. Which means a general harness has a hole exactly where the definition of done should be, and something has to fill it.\u003C/p>\u003Cp>You fill it. Every correction you type is the specification component of a harness that has no other source for it, and no memory of it once the conversation ends. That is not a complaint about copilots, it is
1355what a general harness is: it borrows its standards from whoever happens to be driving.\u003C/p>\u003Cp>An Engineering AI is the same architecture with that hole closed. The domain is fixed, so the harness knows which artifacts a finished pipeline has and can check for them without being asked. The model inside is replaceable and getting cheaper every quarter. The harness is the product.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"the-other-shape\">The other shape\u003C/h2>\u003Cp>Concretely, when a system generates a class of artifact from only one domain, the conventions stop being instructions and become part of what the thing is. The encoding is pinned because that is what a pipeline is. The tests cover the malformed case because a deliverable without that is not finished. There is nothing to ask for, because asking is only necessary when the tool could reasonably do otherwise.\u003C/p>\u003Cp>That constraint is the whole mechanism, and it is why the honest name for this is not \"better AI\". It is narrowness. A tool that produces one artifact can be held to a definition of done for that artifact. A tool that produces anything cannot be held to a definition of anything, which is exactly why a copilot hands you a starting point and leaves the remaining work with you.\u003C/p>\u003Cp>The work does not disappear either way, it moves. With a copilot, your work is directing. With a specialised generator, your work is deciding whether the result is acceptable, which is a smaller job and a more interesting one.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"when-the-treadmill-is-the-right-thing-to-be-on\">When the treadmill is the right thing to be on\u003C/h2>\u003Cp>None of this is an argument against copilots, and the case for them is not weak.\u003C/p>\u003Cp>Work that is different every time has no conventions to encode. Exploratory work, where the point is to find out what you are building, cannot be generated against a definition of done because the definition arrives last. A one-off script has no second instance to amortise anything over. In all of those, a general tool driven by a person who knows what they want is the correct arrangement, and building a generator for it would be an expensive mistake.\u003C/p>\u003Cp>The treadmill is only a problem when the work repeats. And it is worth being precise about the test, because \"repetitive\" is used loosely.\u003C/p>\u003Cp>The work repeats when a new instance shares the same skeleton and differs only in details, when you can write down what a finished instance contains, and when the ways instances differ from each other form a list you could finish. Ingestion passes all three, which is why generating it works. Business logic fails the first, which is why it does not.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"the-question-that-separates-them\">The question that separates them\u003C/h2>\u003Cp>Not which tool is smarter. This one:\u003C/p>\u003Cp>Am I giving instructions I have given before?\u003C/p>\u003Cp>If the answer is no, you are exploring, and a copilot is the right tool. If the answer is yes, and it is yes for most of the code most teams write, then you have a specification you have never written down, and you are re-typing it for the rest of your career.\u003C/p>\u003Cp>The future everyone describes is AI that writes more code. The one that matters is AI y
1355ou stop having to instruct.\u003C/p>\u003Cp>\u003C/p>\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai is an Engineering AI that generates ingestion and integration code. The conventions are not prompts, they are what the output is: executable code, tests that assert behaviour, quality checks per record, an output schema, configuration and documentation.\u003C/em>\u003C/p>\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-09-01T08:43:20.000+00:00","reading_time":5,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Data Pipeline Vendor Lock-In Is Built One Connector at a Time","slug":"data-pipeline-vendor-lock-in-is-built-one-connector-at-a-time","excerpt":"Lock-in is not a contract term, it is a rewrite estimate. Why the ingestion layer accumulates it faster than anything else, and how to measure yours today.","html":"\u003Cp>Most conversations about vendor lock-in are about contracts. Term length, exit clauses, egress fees, proprietary formats. Those are the visible part, and they are the part a procurement team can negotiate.\u003C/p>\u003Cp>The part nobody negotiates is the one that decides whether leaving is possible at all. If you gave notice on Monday, what would you have to rebuild before you could run again?\u003C/p>\u003Cp>That number is your lock-in. Everything else is a line item.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"1-lock-in-is-a-rewrite-estimate-not-a-contract-term\">1. Lock-in is a rewrite estimate, not a contract term\u003C/h2>\u003Cp>The reason the contract framing misleads is that the expensive thing is never the data. Data moves. Storage is a solved problem, formats are increasingly open, and a bulk export is a weekend at worst.\u003C/p>\u003Cp>What does not move is the logic that turned a source into something usable. The connector that knows this API paginates by page number. The rule that a null customer reference means the record predates the migration and should be routed, not dropped. The knowledge that this vendor sends dates in two formats depending on which of their systems generated the row.\u003C/p>\u003Cp>None of that is data. All of it is code, or worse, configuration inside somebody's product. And the cost of leaving is the cost of writing it again, by peo
1355ple who no longer remember why it was written the first time.\u003C/p>\u003Cp>So the useful question is not what the exit costs in euros. It is how many engineering days sit between the decision to leave and the first correct run afterwards.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"2-why-the-ingestion-layer-accumulates-it-faster-than-anything-else\">2. Why the ingestion layer accumulates it faster than anything else\u003C/h2>\u003Cp>Three properties combine here that do not combine anywhere else in the stack:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>You add connectors one at a time, and each one is small:\u003C/strong> Nobody convenes a review to add the 14th source. The decision is made by whoever had the ticket, in an afternoon, using whatever the platform makes easy. Fifty afternoons later the accumulation is significant and no single afternoon was wrong.\u003C/li>\u003Cli>\u003Cstrong>Every connector encodes decisions that nobody records:\u003C/strong> What happens to a row with no primary key. Whether a field that arrives as a string where it was an object gets coerced, rejected, or split. How nested arrays are flattened. What counts as a failed record. Each of these is a semantic choice with downstream consequences, and when a managed platform makes it for you, it is made silently and correctly according to that platform's opinion.\u003C/li>\u003Cli>\u003Cstrong>The decisions become load-bearing without anyone deciding they should:\u003C/strong> The moment a dashboard, a model, or a finance report depends on a number, the policy that produced that number is frozen. Changing it later is not a refactor, it is a discrepancy someone has to explain.\u003C/li>\u003C/ul>\u003Cp>Put together: the ingestion layer is where your business logic goes to hide. Which is another way of saying the ingestion layer is where the decisions with consequences live: if you cannot read them, you do not own them.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"3-the-inventory-and-the-answer-you-do-not-want\">3. The inventory, and the answer you do not want\u003C/h2>\u003Cp>Run those five questions against your own stack, honestly, and the total tells you what you would rewrite.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>What\u003C/th>\n\u003Cth>The portable answer\u003C/th>\n\u003Cth>The answer that becomes a rewrite\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Connectors\u003C/strong>\u003C/td>\n\u003Ctd>Source code you hold, in a repository you control\u003C/td>\n\u003Ctd>Configuration inside a vendor's catalogue\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Transformations\u003C/strong>\u003C/td>\n\u003Ctd>Portable code or SQL you could run elsewhere\u003C/td>\n\u003Ctd>Expressed in a proprietary runtime's dialect\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Quality rules\u003C/strong>\u003C/td>\n\u003Ctd>Version-controlled next to the code\u003C/td>\n\u003Ctd>Clicked into a user interface\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Orchestration\u003C/strong>\u003C/td>\n\u003Ctd>A definition you own and could execute elsewhere\u003C/td>\n\u003Ctd>A managed scheduler with no meaningful export\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Lineage and history\u003C/strong>\u003C/td>\n\u003Ctd>Reproducible from the repository\u003C/td>\n\u003Ctd>Visible only in a vendor console\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>Every row where your honest answer is in the right-hand column is work you would do again. Count it in engineering days. That total, not the contract, is your switching cost.\u003C/p>\u003Cp>Two things usually surprise people running this for the first time. The connectors are rarely the biggest number, because a connector is a bounded piece of work. The quality rules are, because they accumulated over years, they were never written down anywhere else, and the person who knew why rule 31 exists has left.\u003C/p>\u003Cp> \u003C/p>
1355\n\u003Ch2 id=\"4-three-things-that-look-like-portability-and-are-not\">4. Three things that look like portability and are not\u003C/h2>\u003Cp>Each of these appears in vendor material and each is true in a narrow sense that does not survive contact with an actual migration.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>\"We use open formats:\"\u003C/strong> Your storage is portable. Parquet is Parquet and Iceberg is Iceberg. The logic that wrote into it is not, and the logic is what you would rebuild. Open storage formats solve the problem that was already easiest.\u003C/li>\u003Cli>\u003Cstrong>\"You can export your configuration:\"\u003C/strong> A YAML file you cannot execute anywhere is a description of your pipeline, not your pipeline. Ask what runs it after the export. If the answer is only the product you are leaving, you have exported a document.\u003C/li>\u003Cli>\u003Cstrong>\"It is open source:\"\u003C/strong> Self-hostable and portable are different properties. An open-source connector still targets that project's interfaces, its state model, its idea of what a sync is. You have removed the licence risk and kept the rewrite. That is worth something, and it is worth less than it sounds.\u003C/li>\u003C/ul>\u003Cp>The test that cuts through all three: name the runtime you would move to, and ask what percentage of the artifact runs there unmodified.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"5-what-changes-in-january-2027-and-what-it-does-not-fix\">5. What changes in January 2027, and what it does not fix\u003C/h2>\u003Cp>The EU Data Act makes portability a binding right rather than a commercial courtesy. Its switching provisions have applied since September 2025, and from 12 January 2027 providers are generally no longer permitted to charge for switching, including data egress and transit charges.\u003C/p>\u003Cp>This is a real change and it is worth planning around. It is also narrower than the headlines suggest, for a reason that follows directly from section 1.\u003C/p>\u003Cp>The law removes the fee. It does not remove the rewrite.\u003C/p>\u003Cp>After January 2027 your exit gets cheaper in euros and stays exactly as expensive in engineering days. A right to leave is worth what your technical ability to exercise it is worth, and that ability is decided by choices you already made, one connector at a time, in afternoons nobody reviewed.\u003C/p>\u003Cp>Which is the argument for running the inventory in section 3 now rather than in 2027. The regulation gives you the door. Whether you can walk through it is a property of your own repository.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"6-measuring-your-own-number-in-an-afternoon\">6. Measuring your own number in an afternoon\u003C/h2>\u003Cp>You do not need a full audit, you need an estimate good enough to argue with.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Pick your 3 most business-critical sources:\u003C/strong> Not the 3 most complex, the 3 whose numbers appear in front of someone who matters.\u003C/li>\u003Cli>\u003Cstrong>For each, list what you would have to rebuild\u003C/strong> to produce the same output on a different runtime. Connector, transformations, quality rules, schedule, alerting, and the historical backfill.\u003C/li>\u003Cli>\u003Cstrong>Estimate it in engineering days\u003C/strong>, using \u003Ca href=\"https://prettywhale.ai/blog/estimating-data-ingestion-work-a-unit-of-work-method\" rel=\"noopener nofollow\">your own delivery history rather than a benchmark\u003C/a>.\u003C/li>\u003Cli>\u003Cstrong>Multiply by your source count\u003C/strong>, with a discount for the ones that are genuinely simple. The result is wrong in the third digit and right in the order of magnitude, which is all a decision needs.\u003C/li>\u003Cli>\u003Cstrong>Write the number down and date it.\u003C/strong> Then look at it again at the next renewal, which is the only moment anyone has leverage.\u003C/li>\u003C/ul>\u003Cp>The teams that do this find one of two things. Either the number is small, in which case a managed platform is a good deal and they should stop worrying about it. Or the number is large and nobody knew, which is the situation this article is about.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Lock-in in a data platform is not a clause you agreed to. It is an accumulation you never measured, built one connector at a time by peo
1355ple making reasonable local decisions.\u003C/p>\u003Cp>The measurement is a rewrite estimate, in engineering days, for the sources that matter. The five inventory questions get you there in an afternoon: where do the connectors live, where do the transformations live, where do the quality rules live, who owns the orchestration definition, and can the history be reproduced from a repository.\u003C/p>\u003Cp>The regulatory change arriving in January 2027 removes the exit fee. It leaves the rewrite untouched, which makes the rewrite the only number worth knowing.\u003C/p>\u003Cp> \u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai produces the ingestion deliverable as portable code the client owns and runs: the pipeline, its tests, its quality transformations, its output schema, its configuration and its documentation. There is no vendor in the data path, no volume meter, and nothing to export, because nothing was ever held elsewhere. The trade is explicit: you own the operation, and in exchange the decisions that shape your data are in a repository you can read.\u003C/em>\u003C/p>\u003Cp> \u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-08-26T09:46:38.000+00:00","reading_time":5,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Small Language Models for Code Generation: When Narrow Beats Large","slug":"small-language-models-for-code-generation-when-narrow-beats-large","excerpt":"Small language models can match large ones on a bounded job like code generation, at 10 to 30 times lower serving cost. Here is when that trade holds.","html":"\u003Cp>Most teams building on language models start from the same place: use the best available model, meaning the largest and most capable one the budget allows. It is a sensible starting point and it is almost never revisited, which turns it from a decision into a default.\u003C/p>\u003Cp>There is a serious argument that the default is wrong for a large class of systems, and that \u003Cstrong>small language models\u003C/strong> are the better substrate whenever the job is bounded. Code generation for a single domain is about as bounded as a job gets, which makes it the clearest case to examine. Small model, narrow job.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"1-what-the-research-actually-says\">1. What the research actually says\u003C/h2>\u003Cp>In \u003Ca href=\"https://arxiv.org/abs/2506.02153?ref=prettywhale.ai\" rel=\"noreferrer\">\u003Cem>Small Language Models are the Future of Agentic AI\u003C/em>\u003C/a> (Belcak et al., NVIDIA Research and Georgia Tech, arXiv:2506.02153v2, September 2025), the authors take the position that small models rather than large ones are the right substrate for agentic systems. Their working definition of a small model is practical rather than numeric: one that fits on a common consumer device and answers a single user's request with usable latency. As of 2025 they placed that boundary around 10 billion parameters.\u003C/p>\u003Cp>Three claims carry the argument, and the figures are theirs.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Capability is often sufficient:\u003C/strong> They cited Phi-2 at 2.7B reaching commonsense reasoning and code generation scores on par with 30B models while running roughly 15 times faster, Phi-3 small at 7B reaching parity with 70B models on code generation, and distilled 7B reasoning models outperforming large proprietary ones on specific tasks.\u003C/li>\u003Cli>\u003Cstrong>The economics are not close:\u003C/strong> They put serving a 7B model at 10 to 30 times cheaper than a 70 to 175B model in latency, energy and FLOPs. Fine-tuning, they note, becomes a matter of a few GPU-hours rather than a project.\u003C/li>\u003Cli>\u003Cstrong>Breadth is largely wasted:\u003C/strong> Agents exercise a narrow subset of what a general model can do. You pay for the whole capability surface and use a corner of it.\u003C/li>\u003C/ul>\u003Cp>They also quantify the opportunity on real systems, estimating that roughly 60% of the model calls in MetaGPT, 40% in Open Operator and 70% in Cradle could be handled by specialized small models.\u003C/p>\u003Cp>The point of citing this is not that a paper settles the question. It is that the default deserves an argument, and there is a serious one on the other side.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"2-why-code-generation-is-the-strongest-case-for-a-small-model\">2. Why code generation is the strongest case for a small model\u003C/h2>\u003Cp>Among the paper's arguments, one applies to code generation with more force than to anything else, and it is the one least discussed.\u003C/p>\u003Cp>Agentic systems need \u003Cstrong>strict format adherence\u003C/strong>. A tool call has to be exactly well-formed. Generated code has to parse, satisfy an interface, and fit a project structure. The authors argue that models specialized for a single output format outperform generalists at producing it reliably, and that this reliability matters more in these systems than nuanced understanding does.\u003C/p>\u003Cp>That maps onto code generation better than onto conversation. When the output is a runnable project, \"usually correct in shape\" is a failure mode rather than a quality level. A missing import, a signature that does not match the interface, a test that references a method the code does not expose, and the deliverable does not run. The consequence of a formatting near-miss is total rather than graded.\u003C/p>\u003Cp>The corollary is uncomfortable for the default: a larger model with broader knowledge and a slightly less reliable grip on your exact output shape is worse for this job than a smaller one that has only ever produced that shape.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"3-a-domain-narrow-enough-to-generate-is-narrow-enough-to-train-for\">3. A domain narrow enough to generate is narrow enough to train for\u003C/h2>\u003Cp>This is where the two questions turn out to be one.\u003C/p>\u003Cp>We wrote elsewhere about what makes a domain narrow enough for specialized generation: structural repetition, a definition of done you can write, mechanical verification, and a boun
1355ded vocabulary of variation.\u003C/p>\u003Cp>Those same four properties are what make a domain trainable with a small model.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Structural repetition\u003C/strong> gives you a training target that recurs, so the model learns a shape rather than a distribution of possibilities.\u003C/li>\u003Cli>\u003Cstrong>A writable definition of done\u003C/strong> gives you a label. You can tell whether an output is correct, which is the prerequisite for improving on it systematically.\u003C/li>\u003Cli>\u003Cstrong>Mechanical verification\u003C/strong> gives you an automated reward signal, and the ability to reject bad outputs without a human in the loop.\u003C/li>\u003Cli>\u003Cstrong>A bounded vocabulary of variation\u003C/strong> bounds the capability surface you actually need, which is the entire argument for not paying for the rest.\u003C/li>\u003C/ul>\u003Cp>A domain that fails those tests needs a general model, because you cannot specify what you want well enough to specialize for it. A domain that passes them makes the large model an expensive way to buy capability you will not use.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"4-what-you-give-up\">4. What you give up\u003C/h2>\u003Cp>The trade is real and we can state it:\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>What you lose\u003C/th>\n\u003Cth>Consequence\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Breadth\u003C/strong>\u003C/td>\n\u003Ctd>Outside the domain, the model does nothing useful. There is no graceful degradation into general helpfulness\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>The novel case\u003C/strong>\u003C/td>\n\u003Ctd>A source or a requirement genuinely unlike anything in the domain gets handled worse than a general model would handle it\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Conversational latitude\u003C/strong>\u003C/td>\n\u003Ctd>You cannot ask it to explain, to refactor for style, or to answer a question about the code. Those are different jobs\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>The escape hatch\u003C/strong>\u003C/td>\n\u003Ctd>With a general model, an unanticipated request produces something. With a specialist, it will probably produces nothing useful, and the system has to route elsewhere\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>The last row is the one to design for rather than to argue away. The paper's own answer is heterogeneity: different models at different decision points, a specialist where the format matters and a general model where the request is open-ended. That is an architecture choice, not a compromise, and it is what makes the migration incremental rather than a bet.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"5-how-you-would-test-this-yourself\">5. How you would test this yourself\u003C/h2>\u003Cp>The paper proposes a six-step path, and it is worth reading as an evaluation method rather than as a migration plan. Condensed, with the parts that matter for a team considering this:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Log what you actually send:\u003C/strong> Prompts, responses, tool calls, latency. Most teams running on a general model have no record of the distribution of requests they make, which means they cannot tell how much breadth they are using.\u003C/li>\u003Cli>\u003Cstrong>Cluster the requests:\u003C/strong> Unsupervised clustering on the prompts tells you how many distinct jobs you really have. The answer is usually far smaller than the capability you are paying for.\u003C/li>\u003Cli>\u003Cstrong>Take the largest cluster and only that one:\u003C/strong> Fine-tune a candidate small model on it. The paper puts the useful dataset size at 10,000 to 100,000 examples and the technique at LoRA or full fine-tuning depending on scale.\u003C/li>\u003Cli>\u003Cstrong>Compare on your own acceptance criteria\u003C/strong>, not on public benchmarks. What matters is whether the output clears the bar you would apply to a colleague's work.\u003C/li>\u003C/ul>\u003Cp>The first two steps cost days rather than quarters, and they answer the question that decides the rest: how much of your general model's range are you actually exercising.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"6-the-cost-question-a-cto-will-actually-ask\">6. The cost question a CTO will actually ask\u003C/h2>\u003Cp>The 10 to 30 times figure is about serving. It is not the number that decides anything, because nobody's bill is only inference.\u003C/p>\u003Cp>
1355Four cost lines move in opposite directions when you go from an API call to a model you run:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Inference goes down\u003C/strong>, by roughly the factor above, and it keeps going down as volume grows because you are no longer paying per token.\u003C/li>\u003Cli>\u003Cstrong>Operations goes up:\u003C/strong> Somebody hosts, monitors, patches and capacity-plans a model. That line did not exist before and it does not shrink with volume.\u003C/li>\u003Cli>\u003Cstrong>Iteration cost goes down:\u003C/strong> The paper puts fine-tuning at a few GPU-hours, which changes what you are willing to try. A model you can retrain in an afternoon gets improved. A model you can only prompt gets worked around.\u003C/li>\u003Cli>\u003Cstrong>Switching cost goes up:\u003C/strong> A specialized model is an asset you own and also a commitment. Changing your mind means retraining rather than changing a string.\u003C/li>\u003C/ul>\u003Cp>Which way the sum falls depends almost entirely on volume and on whether the operations line is already staffed. At low volume with no platform team, the API call is correct and the argument in this article does not apply to you. At high volume on a narrow job, the crossover arrives sooner than most teams assume, and the iteration line is the one usually left out of the comparison.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"7-where-the-argument-is-weakest\">7. Where the argument is weakest\u003C/h2>\u003Cp>The authors are explicit about the counter-arguments:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Economy of scale is a real objection:\u003C/strong> Centralized endpoints amortize infrastructure across enormous load, and the authors concede the economics are highly case-specific. Self-hosting a small model shifts cost from a per-token line to an operations line, and that trade is not automatically favourable.\u003C/li>\u003Cli>\u003Cstrong>No timeline is offered:\u003C/strong> They explicitly decline to predict when adoption happens, and they name the barriers: tens of billions already invested in centralized hosting, benchmarks that measure generalist ability rather than agentic reliability, and simple lack of attention.\u003C/li>\u003Cli>\u003Cstrong>Not everything is replaceable:\u003C/strong> Their own framing keeps general models in the architecture for the open-ended parts. The claim is about the majority of calls, not all of them.\u003C/li>\u003C/ul>\u003Cp> \u003C/p>\n\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Reaching for the largest available model is a default, and defaults deserve one examination.\u003C/p>\u003Cp>The examination is cheap: log the requests you actually make, cluster them, and see how many distinct jobs there are. If the answer is a handful, you are paying for a capability surface you are not using, and the research suggests the specialized alternative is 10 to 30 times cheaper to serve at comparable quality on those jobs.\u003C/p>\u003Cp>The condition is that your domain has to be specifiable. If you cannot write down what a correct output looks like, you cannot specialize, and the general model is the honest tool. If you can, the size of the model stops being a proxy for the quality of the result.\u003C/p>\u003Cp> \u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai is an Engineering AI built on a specialized small language model trained for one job: generate ingestion and integration code. You give it one sample of a data source and it returns a complete production-ready pipeline : executable code, unit tests, data quality checks, schemas, configuration and documentation. One bundle with every artifacts, the same shape every time, in standard languages.\u003C/em>\u003C/p>\u003Cp> \u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-08-20T09:27:33.000+00:00","reading_time":6,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"Who Accepts AI-Generated Code That Nobody Wrote","slug":"who-accepts-code-nobody-wrote","excerpt":"Code review assumes an author who can answer for the code. Generated code breaks that assumption. What changes in the pull request, and after an incident.","html":"\u003Cp>Code review works on an assumption nobody states. Somewhere there is an author, and the author can answer for the code: why this approach, what happens in that case, what they considered and rejected. The reviewer's job is to interrogate that reasoning, and the author's ability to defend it is most of what makes approval mean something.\u003C/p>\u003Cp>Generated code removes the author. The person submitting it can tell you what they asked for. They often cannot tell you why line 47 handles the case that way, because nobody decided it.\u003C/p>\u003Cp>That does not make the code worse. It makes approval rest on something that has to be named, because the old basis is gone and nothing replaced it by default.\u003C/p>\u003Ch2 id=\"1-three-bases-for-ai-generated-code-acceptance\">1. Three bases for AI-generated code acceptance\u003C/h2>\u003Cp>There are only three answers to who accepts, and each has a cost. Choosing deliberately beats discovering which one you drifted into.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The prompter accepts:\u003C/strong> Whoever ran the generation owns the output. Simple, matches how pull requests already work, and it is what most teams do without deciding to.The problem is that it asks for accountability without the means. A person who cannot answer for line 47 is being asked to stand behind it anyway, and the predictable result is either genuine risk transfer to someone who cannot carry it, or a quiet fiction everyone maintains.\u003C/li>\u003Cli>\u003Cstrong>The reviewer accepts:\u003C/strong> The approver owns it, on the basis of having read it.Honest, and expensive in the one resource you have least of. It also runs into the completeness problem: a finished-looking deliverable of 20 files gets skimmed, not read, and the approval means less than it appears to.\u003C/li>\u003Cli>\u003Cstrong>The check accepts:\u003C/strong> The deliverable is accepted because it satisfies a specified set of properties, verified mechanically. A person owns the specification and owns the decision to trust it, rather than owning each line.This is the only one that scales, and it relocates accountability rather than dissolving it. Somebody wrote the contract, somebody decided it was sufficient, and both of those are answerable positions.\u003C/li>\u003C/ul>\u003Cp>
1355Most mature arrangements combine the third for the mechanical properties and the second for the judgement, which is a division of labour rather than a compromise.\u003C/p>\u003Ch2 id=\"2-what-changes-in-the-pull-request\">2. What changes in the pull request\u003C/h2>\u003Cp>Four practical shifts, and the first one is the whole article in a sentence.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The question moves from \"why did you do it this way\" to \"what did you verify\":\u003C/strong> The first is increasingly unanswerable and the second is always answerable. It is also the better question, because it produces a checkable claim rather than a narrative.\u003C/li>\u003Cli>\u003Cstrong>The description carries the verification, not the reasoning:\u003C/strong> What was generated, from what input, which checks ran, what the reviewer looked at by hand. That is a record you can consult after an incident, which a rationale is not.\u003C/li>\u003Cli>\u003Cstrong>Size stops indicating effort:\u003C/strong> Reviewers have always used volume as a proxy for how much care went in, and therefore for how carefully to read. A 900-line change produced in 20 minutes breaks the proxy. Read by risk instead: the boundary with your system, the defaults, the error paths.\u003C/li>\u003Cli>\u003Cstrong>The presence of tests stops being evidence:\u003C/strong> It used to signal that somebody thought about failure modes, because writing them forced it. Now it signals that tests were requested. What they assert is the only thing that carries information.\u003C/li>\u003C/ul>\u003Ch2 id=\"3-the-question-that-arrives-after-an-incident\">3. The question that arrives after an incident\u003C/h2>\u003Cp>This is the part worth settling before you need it, because the conversation goes badly when it is improvised.\u003C/p>\u003Cp>A pipeline built from generated code corrupts a month of data. In the review, the meeting or the postmortem, somebody asks who approved this.\u003C/p>\u003Cp>Under the first basis, the answer is a person who could not have caught it. That is an unfair outcome and it produces the wrong lesson: people become reluctant to submit generated work, or they submit it with a disclaimer, and neither improves anything.\u003C/p>\u003Cp>Under the third basis, the answer is different and more useful. The deliverable satisfied the contract, and the contract did not cover the failure. That points at a gap in the specification, which is fixable, rather than at a lapse in attention, which is not.\u003C/p>\u003Cp>The second answer is also the honest one in most real cases. Reviewers rarely catch the defect that causes the incident. They catch the ones that are visible, and the incident is almost always caused by something that was not.\u003C/p>\u003Cp>Two things follow from choosing the third basis deliberately.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The contract has to be owned by a named person:\u003C/strong> An unowned specification decays, and after an incident an unowned specification means nobody is accountable, which is worse than the situation you started from.\u003C/li>\u003Cli>\u003Cstrong>A gap found by an incident becomes a clause:\u003C/strong> That is the loop that makes the arrangement improve. Without it, the contract is a snapshot of what you knew before your first serious failure.\u003C/li>\u003C/ul>\u003Ch2 id=\"4-what-to-write-down-now\">4. What to write down now\u003C/h2>\u003Cp>Four things, and none of them takes longer than a meeting.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Which basis you are using\u003C/strong>, per category of work. Generated ingestion code might be accepted on checks. A change to authentication might require a reviewer who read every line. Both are defensible, and the mistake is having no stated answer, because then the answer is whichever one is convenient after the fact.\u003C/li>\u003Cli>\u003Cstrong>Who owns the contract:\u003C/strong> One name, with time allocated to maintain it.\u003C/li>\u003Cli>\u003Cstrong>What the pull request has to state:\u003C/strong> What was generated, from what, which checks ran, what was reviewed by hand. Four lines in a template.\u003C/li>\u003Cli>\u003Cstrong>What the reviewer is still responsible for:\u003C/strong>
1355 Explicitly, so it is small and real rather than implicit and unbounded. Usually: the boundary with your system, the defaults, whether the thing solves the right problem.\u003C/li>\u003C/ul>\u003Cp>That last item is worth protecting. A reviewer with a short, explicit, genuinely important list does that job well. A reviewer nominally responsible for everything does none of it, and everybody knows it.\u003C/p>\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Review used to rest on an author who could answer for the code. Generated code removes the author, and the basis for approval has to be replaced rather than assumed.\u003C/p>\u003Cp>Three bases exist. The prompter, which asks for accountability without the means. The reviewer, which is honest and does not scale. The check, which scales and moves accountability to whoever wrote and accepted the specification.\u003C/p>\u003Cp>Pick one per category of work, name the owner of the contract, and shorten what the reviewer is responsible for until it is a list they can actually honour. Then change the question in review from why you did it this way to what you verified.\u003C/p>\u003Cp>The second question is answerable, checkable, and it was always the better one.\u003C/p>\u003Cp> \u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai produces artifacts an acceptance test needs: a full test suite, a machine-readable output schema, documentation of the failure behaviour. The point is not that review disappears. It is that approval can rest on verified properties rather than on a rationale nobody can supply.\u003C/em>\u003C/p>\u003Cp> \u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-08-17T12:11:09.000+00:00","reading_time":4,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"Production-Ready Data Pipelines: What It Means and Who Decides","slug":"what-production-ready-means-for-a-data-pipeline-and-who-decides","excerpt":"Ask two engineers when an ingestion pipeline is finished and you get two answers. Here is how to write the bar down on one page and enforce it in review.","html":"\u003Cp>Ask two engineers on the same team when an ingestion pipeline is finished.\u003C/p>\u003Cp>The first says it is finished when it runs correctly on the source. The second says it is finished when it runs correctly on the source, handles malformed records, has tests, has a documented output contract, and can be picked up by someone else. Both answers are reasonable. They differ by a factor of three in effort and by an order of magnitude in maintenance cost.\u003C/p>\u003Cp>The disagreement is rarely had out loud, which is why it produces a different pipeline every time depending on who was assigned and how much sc
1355hedule pressure they were under. Writing it down is a one-page document and a meeting. It is also the cheapest intervention available on delivery quality, and the one most often skipped in favour of buying a tool.\u003C/p>\u003Ch2 id=\"1-what-the-bar-actually-is-artefact-by-artefact\">1. What the bar actually is, artefact by artefact\u003C/h2>\u003Cp>A definition of done that says \"code, tests and documentation\" is not a definition, because every one of those words means something different to each reader. The bar has to be specific enough that two people can look at the same pipeline and agree on whether it clears.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Code:\u003C/strong> runs on the full source, not on a sample. Handles the failure modes named in your team's list. Configuration externalized rather than hardcoded.\u003C/li>\u003Cli>\u003Cstrong>Tests:\u003C/strong> every transformation has a test for a valid input, a null input, and a malformed input. The malformed case asserts the behaviour, not just the absence of a crash. Coverage target stated as a number your team agreed on.\u003C/li>\u003Cli>\u003Cstrong>Data quality checks:\u003C/strong> completeness, uniqueness of the claimed key, value ranges, expected volume. Failures recorded per record rather than logged and forgotten.\u003C/li>\u003Cli>\u003Cstrong>Output contract:\u003C/strong> a machine-readable schema of what the pipeline emits, checked in CI. Nullable fields state the conditions under which they are null.\u003C/li>\u003Cli>\u003Cstrong>Configuration:\u003C/strong> every path, threshold, credential reference and error policy lives outside the code.\u003C/li>\u003Cli>\u003Cstrong>Orchestration:\u003C/strong> schedule, dependencies, retry policy, and what happens on failure, including who is notified.\u003C/li>\u003Cli>\u003Cstrong>Documentation:\u003C/strong> answers what it reads, what it produces, what happens to a bad record, what happens if it runs twice, and how to add a field.\u003C/li>\u003C/ul>\u003Ch2 id=\"2-who-decides-when-the-engineer-and-the-manager-disagree\">2. Who decides when the engineer and the manager disagree\u003C/h2>\u003Cp>This is the part a checklist cannot solve, and it is where most definitions of done quietly die.\u003C/p>\u003Cp>The engineer wants to add the tests. The delivery manager needs the pipeline on Thursday. Both are doing their job. If the answer is decided case by case, the definition of done is decorative, because it loses every time there is pressure, and there is always pressure.\u003C/p>\u003Cp>Three arrangements work, in ascending order of maturity:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The bar is not negotiable, the scope is:\u003C/strong> The definition of done applies to whatever ships. If Thursday is immovable, fewer sources ship on Thursday. This is the cleanest rule and the hardest to hold the first few times.\u003C/li>\u003Cli>\u003Cstrong>Named exceptions with an expiry:\u003C/strong> Something ships below the bar, the gap is written down with a date, and the date is tracked like any other commitment. Honest, and it works as long as the list gets reviewed. If nobody reviews it, this is just the previous arrangement with extra paperwork.\u003C/li>\u003Cli>\u003Cstrong>A tech lead with the final call:\u003C/strong> One person owns the bar and can say no. Requires that the organization actually backs them the first time they use it, which is the real test.\u003C/li>\u003C/ul>\u003Cp>What does not work is collective ownership with no arbitrator. When everyone owns the bar, the bar is set by whoever is most tired that week.\u003C/p>\u003Cp>One framing that helps in the conversation with delivery: the bar is not a quality preference, it is a cost decision about a later quarter. Shipping below it moves work forward in the calendar and multiplies it. That argument lands better than an appeal to craftsmanship, because it is in the currency the other person is managing.\u003C/p>\u003Ch2 id=\"3-building-your-teams-failure-mode-list\">3. Building your team's failure-mode list\u003C/h2>\u003Cp>The generic bar above applies anywhere. The part that makes a definition of done yours is the list of failure modes your sources actually produce, and it has to come from your own incidents rather than from an article.\u003C/p>\u003Cp>Build it once, in an hour:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Take the last 10 production incidents\u003C/strong> on ingestion: Tickets, alerts, or the memory of whoever was on call.\u003C/li>\u003Cli>\u003Cstrong>For each, write the one sentence that describes what the code should have done:\u003C/strong> Not what went wrong, what the handling should have been. \"A record with an unparseable date should have been flagged and skipped, not aborted the batch.\"\u003C/li>\u003Cli>\u003Cstrong>Group them:\u003C/strong> Most teams end up with 5 to 8 distinct modes, and the same three account for the majority of incidents.\u003C/li>\u003Cli>\u003Cstrong>Put the list in the definition of done\u003C/strong>, as the things every pipeline is expected to handle.\u003C/li>\u003C/ul>\u003Cp>Two properties make this list valuable in a way a generic checklist is not. It is specific to the sources you actually ingest, so nobody can argue it is theoretical. And it is short, because your incidents cluster, which means the bar stays achievable.\u003C/p>\u003Cp>Review it after each incident that is not on the list. Either the mode is new and it joins, or the handling was there and failed, which is a different problem.\u003C/p>
1355\u003Ch2 id=\"4-writing-it-on-one-page\">4. Writing it on one page\u003C/h2>\u003Cp>Longer than a page and it stops being read. The structure that holds:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The list of artefacts\u003C/strong>, with the specific bar for each, in the form of section 1 rather than in the form of adjectives.\u003C/li>\u003Cli>\u003Cstrong>The failure modes your sources actually produce\u003C/strong>, named. Every team has its own list, and it comes from your incident history rather than from a generic article. Five to eight items.\u003C/li>\u003Cli>\u003Cstrong>What is explicitly out of scope\u003C/strong>, which prevents the definition from expanding into an unbounded quality manifesto.\u003C/li>\u003Cli>\u003Cstrong>The exception process\u003C/strong>, in two sentences.\u003C/li>\u003C/ul>\u003Cp>Write it with the two engineers whose work the team already respects, not alone. A standard extracted from practice gets adopted. A standard handed down gets routed around, and the technical content is often identical in both cases.\u003C/p>\u003Cp>Date it, and put a name on it. An undated document with no owner decays into folklore within a year.\u003C/p>\u003Ch2 id=\"5-enforcing-it-in-review-not-in-documentation\">5. Enforcing it in review, not in documentation\u003C/h2>\u003Cp>A definition of done that lives only in a wiki has no mechanism. The enforcement has to sit in the path the work already takes:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Pull request template\u003C/strong> that lists the artefacts as checkboxes: Cheap, visible, and it makes the omission deliberate rather than accidental.\u003C/li>\u003Cli>\u003Cstrong>CI checks\u003C/strong> for the parts a machine can verify: tests pass, coverage threshold met, schema validates against a sample output.\u003C/li>\u003Cli>\u003Cstrong>Review as the human gate\u003C/strong> for the parts it cannot: whether the tests assert anything meaningful, whether the documentation answers the five questions, whether the failure modes on your list are actually handled.\u003C/li>\u003C/ul>\u003Cp>The CI part matters more than it looks. Anything a machine checks stops being a conversation, which removes it from the negotiation entirely and leaves the human review free for the judgement calls.\u003C/p>\u003Ch2 id=\"6-what-happens-the-first-month\">6. What happens the first month\u003C/h2>\u003Cp>Two things, and both are worth expecting out loud before they happen.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Delivery slows down:\u003C/strong> The team is learning the bar while delivering against it, and the first few pipelines take longer. If nobody says this in advance, the slowdown becomes the evidence that the initiative failed.\u003C/li>\u003Cli>\u003Cstrong>Somebody tests the exception process immediately:\u003C/strong> That first exception decides whether the definition is real. Granting it correctly, with a written gap and a date that gets tracked, establishes the mechanism. Granting it informally establishes that the bar is optional.\u003C/li>\u003C/ul>\u003Cp>After that, the visible effect is not that pipelines get better in a way anyone notices. It is that they stop varying. Estimates start holding, review gets faster because reviewers know what they are looking for, and the pipeline written by whoever was available looks like the pipeline written by anyone else.\u003C/p>\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Two engineers finishing the same work at different points is not a discipline problem. It is a specification problem, and the specification is one page.\u003C/p>\u003Cp>Write the bar artefact by artefact, in terms specific enough that two people agree on whether it clears. Decide in advance who arbitrates under pressure, because the answer decided case by case is always the same one. Enforce it where the work already passes, in the pull request and in CI, rather than in a document nobody opens.\u003C/p>\u003Cp>The teams that skip this and buy tooling instead end up with the same variance, applied faster.\u003C/p>\u003Cp> \u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai produces the full artefact set from a source sample: code, unit tests, data quality checks, output schema, configuration, orchestration and documentation. The definition of done stops being something to enforce and becomes what arrives by default. The bar still has to be written, because somebody has to review the output against it.\u003C/em>\u003C/p>\u003Cp> \u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-08-12T11:37:47.000+00:00","reading_time":5,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Estimating Data Ingestion Work: A Unit-of-Work Method","slug":"estimating-data-ingestion-work-a-unit-of-work-method","excerpt":"Stop estimating ingestion tasks one by one. Estimate units of work, build a tier grid from your own delivery history, and recalibrate it after every project.","html":"\u003Cblockquote>Software estimation is hard everywhere. Data estimation is hard in a specific way that generic estimation advice does not address, and that specificity is the whole problem.\u003C/blockquote>\u003Cp>On application work, the unknown lives in the scope. You reduce it by asking questions: what should this screen do, what happens on error, who can see it. Discovery is a conversation, and a good analyst can close most of the gap before anyone commits to a number.\u003C/p>\u003Cp>On ingestion work, the unknown lives in the data. No conversation closes it. The client genuinely believes the export is clean, because the person you are talking to has never opened it. You find out in week three, and by then the number is already in a signed document.\u003C/p>\u003Cp>This article is a method for producing ingestion estimates that survive contact with the data. It is aimed at whoever actually writes the estimate.\u003C/p>\u003Ch2 id=\"1-stop-estimating-tasks-start-estimating-units-of-work\">1. Stop estimating tasks, start estimating units of work\u003C/h2>\u003Cp>The common approach is a task breakdown. Read the source, two days. Write the transformations, three days. Tests, one day. Documentation, half a day. Total, six and a half days, add a buffer.\u003C/p>\u003Cp>This fails for a reason worth understanding: the breakdown is a description of the work, not a prediction of it. Every line is an estimate of something nobody has looked at yet, and the errors compound in the same direction, because every unexamined line looks simpler than it is.\u003C/p>\u003Cp>The alternative is to estimate at the level of a unit that recurs. In ingestion, that unit is the source.\u003C/p>\u003Cp>One source, ingested to production standard, is a repeatable deliverable. It has variants, and the variants are classifiable. Once you know what a standard-tier source costs you, an eleven-source project is arithmetic rather than a guess.\u003C/p>\u003Cp>This only works if two things are true. The definition of \"to production standard\" has to be written down and identical across sources, otherwise you are averaging different deliverables. And your reference figures have to come from your own history, because nobody else's productivity applies to your team.\u003C/p>\u003Ch2 id=\"2-building-your-tier-grid-from-your-own-history\">2. Building your tier grid from your own history\u003C/h2>\u003Cp>3 tiers because the grid has to be usable in a conversation with a client who is not going to sit through a taxonomy.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Step one: take your last ten to fifteen ingestion deliveries.\u003C/strong> Across clients, same category of work.\u003C/li>\u003Cli>\u003Cstrong>Step two: reconstruct actual effort.\u003C/strong> Not the estimate, the actual. Timesheets if they are trustworthy, otherwise commit history plus a five-minute c
1355onversation with whoever did it. Elapsed engineering effort from source access to production, excluding client waiting time.\u003C/li>\u003Cli>\u003Cstrong>Step three: sort them by effort\u003C/strong>, then look for the natural breaks. There usually are some, and they usually correspond to the characteristics below.\u003C/li>\u003C/ul>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Tier\u003C/th>\n\u003Cth>Characteristics\u003C/th>\n\u003Cth>Your reference effort\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Simple\u003C/strong>\u003C/td>\n\u003Ctd>Documented stable format, fixed schema, single entity, modest volume, full refresh acceptable\u003C/td>\n\u003Ctd>Calibrate from your own history\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Standard\u003C/strong>\u003C/td>\n\u003Ctd>Pagination or incremental logic needed, some ambiguous types, occasional schema shifts, moderate volume\u003C/td>\n\u003Ctd>Calibrate from your own history\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Complex\u003C/strong>\u003C/td>\n\u003Ctd>Proprietary or legacy source, nested structures, no reliable change indicator, large volumes, or business logic to decode\u003C/td>\n\u003Ctd>Calibrate from your own history\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cul>\u003Cli>\u003Cstrong>Step four: write down the classification criteria\u003C/strong>, not just the numbers. In six months someone else will need to classify a source, and if the criteria only exist in your head the grid decays into a lookup table nobody trusts.\u003C/li>\u003C/ul>\u003Cp>The output of this exercise is two hours of work and it is the only credible estimation basis that exists for your team. Published industry benchmarks are worse than useless here, because they average across practices with different definitions of done.\u003C/p>\u003Ch2 id=\"3-the-qualification-phase-in-detail\">3. The qualification phase, in detail\u003C/h2>\u003Cp>This is the part that converts a gamble into an estimate, and it is the part most proposals skip.\u003C/p>\u003Cp>A qualification phase is short and produces a deliverable. Two to five days depending on the number of sources.\u003C/p>\u003Cp>\u003Cstrong>What you do during this phase\u003C/strong>\u003C/p>\u003Cul>\u003Cli>Get real access to every source. Not a specification, not a screenshot. Access.\u003C/li>\u003Cli>Pull a genuine sample from each, large enough to contain the ugly rows. The first hundred records of a file are usually the cleanest hundred records in it.\u003C/li>\u003Cli>Profile each sample: null rates, distinct counts, format variants per field, encoding, volume, and whether the claimed unique identifier is unique.\u003C/li>\u003Cli>Identify the change indicator. Is there a reliable updated-at column, a change log, a sequence. If the answer is no, that source is complex regardless of how simple its format looks.\u003C/li>\u003Cli>Classify each source against your criteria, and record why.\u003C/li>\u003C/ul>\u003Cp>\u003Cstrong>What you deliver\u003C/strong>\u003Cbr>A short document: the inventory of sources with tier and rationale, the profile of each sample, the list of remaining unknowns, and the estimate that follows.\u003C/p>\u003Cp>\u003Cstrong>Why the client could pay for it\u003C/strong>\u003Cbr>Because it has standalone value. It documents their data estate, and it almost always surfaces quality problems they did not know about. More than once the qualification report is the most useful artefact the client receives all quarter, and it is produced before any commitment on the main work.\u003C/p>\u003Cp>\u003Cstrong>The clause that follows from it.\u003C/strong>\u003Cbr>The estimate covers the qualified source, meaning the source as sampled. If what arrives in delivery differs materially from the sample, it gets requalified. Stated up front this reassures rather than alarms, because it demonstrates that you know where the risk lives.\u003C/p>\u003Ch2 id=\"4-what-the-estimate-should-actually-contain\">4. What the estimate should actually contain\u003C/h2>\u003Cp>An estimate that is a single number invites haggling over that number. An estimate that shows its structure moves the conversation to scope.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Component\u003C/th>\n\u003Cth>Why it is a separate line\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Qualification\u003C/strong>\u003C/td>\n\u003Ctd>Delivered before commitment, has standalone value\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Production, per tier, with counts\u003C/strong>\u003C/td>\n\u003Ctd>
1355Client sees the arithmetic: 4 simple, 5 standard, 2 complex\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Review and acceptance\u003C/strong>\u003C/td>\n\u003Ctd>Real work, and the line that protects your quality bar under pressure\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Contingency, stated explicitly\u003C/strong>\u003C/td>\n\u003Ctd>Hidden buffers get discovered and destroy trust. A named contingency gets discussed\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Explicit exclusions\u003C/strong>\u003C/td>\n\u003Ctd>New sources, historical backfill beyond a stated window, downstream modelling, support beyond a stated period\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>The exclusions section is what makes the rest credible. A proposal that only lists what is included reads like optimism.\u003C/p>\u003Cp>One more line worth including on any estimate over a certain size: what the client has to provide, and by when. Source access, credentials, a named technical contact, and a decision on ambiguous business rules. Half of all data project overruns are waiting time, and waiting time that was never named in the estimate becomes your problem.\u003C/p>\u003Ch2 id=\"5-the-recalibration-loop\">5. The recalibration loop\u003C/h2>\u003Cp>An estimation grid built once and never revisited drifts within a year, usually toward optimism.\u003C/p>\u003Cp>After each delivery, record three numbers: the tier you assigned, the effort you estimated, the effort it actually took. It takes 10 minutes per project.\u003C/p>\u003Cp>Every quarter, look at the deltas. You are looking for three patterns.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Systematic underestimation in one tier:\u003C/strong> Adjust the reference figure. This is the normal outcome and it is why the loop exists.\u003C/li>\u003Cli>\u003Cstrong>A tier that no longer separates cleanly\u003C/strong>: If your standard and complex tiers now overlap, your criteria need work, or the shape of your incoming sources has changed.\u003C/li>\u003Cli>\u003Cstrong>A misclassification pattern:\u003C/strong> If the same characteristic keeps pushing sources over their tier, that characteristic belongs in your criteria for the next tier up. The absence of a reliable change indicator is the most common one.\u003C/li>\u003C/ul>\u003Cp>The reward for maintaining this loop is that your contingency shrinks. A practice with three years of calibrated history can quote with a five percent buffer where it once needed thirty, and that difference is either margin or competitiveness, depending on how you choose to spend it.\u003C/p>\u003Ch2 id=\"6-estimating-when-you-have-no-history\">6. Estimating when you have no history\u003C/h2>\u003Cp>The method above assumes around 10 past deliveries. Plenty of practices do not have them, either because the data practice is new or because nobody recorded the effort. Three substitutes, in descending order of reliability.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Borrow from an adjacent capability:\u003C/strong> If your team has built integrations, API work, or file processing, that effort is closer to your ingestion effort than any published benchmark. Adjust for the parts that differ and use it as a starting point.\u003C/li>\u003Cli>\u003Cstrong>Run one deliberately instrumented delivery:\u003C/strong> Take a single source, deliver it to your written standard, and measure carefully. One real data point from your own team beats twenty from someone else's. Sell that first one on time and materials precisely so that the measurement is not distorted by pricing pressure.\u003C/li>\u003Cli>\u003Cstrong>Estimate the qualification only, and stage the commitment:\u003C/strong> Quote the qualification phase firmly, and quote the production work as a range with the commitment deferred until qualification completes. Some clients will refuse. Many will accept, because a firm that says \"here is what I can commit to now and here is what I will commit to in five days\" reads as competent rather than evasive.\u003C/li>\u003C/ul>\u003Cp>What not to do: take a figure from a vendor page or a conference talk and treat it as a reference. Those numbers come from a different team, with a different definition of done, on sources you have not seen. Using them produces an estimate with the appearance of rigour and n
1355one of the substance, which is worse than an honest range.\u003C/p>\u003Ch2 id=\"7-three-estimation-traps-specific-to-ingestion\">7. Three estimation traps specific to ingestion\u003C/h2>\u003Cul>\u003Cli>\u003Cstrong>The clean sample:\u003C/strong> Whatever the client sends you as an example is the cleanest data they have. Always ask for a full period rather than a sample, and if you get a sample, ask specifically for the rows that caused problems downstream.\u003C/li>\u003Cli>\u003Cstrong>The invisible change indicator:\u003C/strong> No reliable way of knowing which records changed since the last run. No updated-at column, no change log, no sequence. That leaves full refresh, or comparison logic. Both are significantly more expensive to build and to run than reading one column. This characteristic alone moves more sources up a tier than any other, and it is the one least likely to come up in a scoping conversation.\u003C/li>\u003Cli>\u003Cstrong>Volume that is not volume yet:\u003C/strong> A source that is small today and growing changes tier at a threshold. Ask about growth, and about any planned acquisition or migration that would multiply it. The cost of finding out later is a rebuild, not an adjustment.\u003C/li>\u003C/ul>\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>To accurately estimate intake, it's not so much a matter of technique as it is of having your own data.\u003C/p>\u003Cp>Reconstruct the actual effort on your last ten to fifteen deliveries and find the natural tier breaks. Write down the classification criteria and not just the figures. Bill a qualification phase if possible and commit against the qualified source rather than the described one. Record estimate versus actual after every delivery, and revise quarterly.\u003C/p>\u003Cp>None of that is difficult. It is just unglamorous, and it is the difference between an estimate you defend and an estimate you trust.\u003C/p>\u003Cp>What you then do with those numbers commercially, including whether to commit them as a fixed price, is a separate decision.\u003C/p>\u003Cp> \u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai produces the complete ingestion deliverable from a source sample: code, tests, quality checks, schemas, configuration and documentation. Two effects on estimation: the definition of done stops varying by engineer, and effort shifts toward qualification and review, which are the parts you can actually scope in advance. If you are working on how your data practice estimates, we can talk.\u003C/em>\u003C/p>\u003Cp> \u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any question on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-08-10T06:31:36.000+00:00","reading_time":7,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Moove-SI and PrettyWhale.ai Team Up on Data & AI","slug":"moove-si-and-prettywhale-ai-team-up","excerpt":"Moove-SI, an integrator specialising in Microsoft solutions, is adding PrettyWhale.ai to its Data & AI offer to speed up the costliest part of data projects.","html":"\u003Ch2 id=\"what-if-ai-could-speed-up-the-most-technical-costly-time-consuming-and-tedious-part-of-data-projects\">\u003Cstrong>What if AI could speed up the most technical, costly, time-consuming, and tedious part of data projects?\u003C/strong>\u003C/h2>\u003Cp>We picture data projects as analysis and dashboards. In practice, most of the time goes somewhere else: collecting the sources, understanding their structure, transforming, validating, making them usable.\u003C/p>\u003Cp>This work is essential, but itâs often time-consuming, complex, and repetitive.\u003C/p>\u003Cp>To tackle this challenge, \u003Cstrong>Moove-SI is partnering with PrettyWhale.ai ð\u003C/strong>\u003C/p>\u003Cp>PrettyWhale.ai develops \u003Cstrong>AI engineering\u003C/strong> \u003Cstrong>technology\u003C/strong> \u003Cstrong>specialized in the automated generation of code for data ingestion and integration\u003C/strong>.\u003C/p>\u003Cp>A general-purpose copilot suggests code to a developer. PrettyWhale.ai automates the entire production chain:\u003C/p>\u003Cul>\u003Cli>â
source data analysis and profiling\u003C/li>\u003Cli>â
target structure and transformation rules\u003C/li>\u003Cli>â
generation of ingestion and integration code\u003C/li>\u003Cli>â
automatic creation of unit and integration tests\u003C/li>\u003Cli>â
generation of configurations and technical documentation\u003C/li>\u003Cli>â
quality and consistency validation of the generated code\u003C/li>\u003C/ul>\u003Cp> \u003C/p>\n\u003Cp>The result is \u003Cstrong>production-grade code that\u003C/strong> is \u003Cstrong>readable, testable, and maintainable\u003C/strong>, remains fully under the control of the technical teams, and integrates with each clientâs architectures and development standards.\u003C/p>\u003Cp>By integrating this technology into its Data & AI offerings, Moove-SI strengthens its ability to:\u003C/p>\u003Cul>\u003Cli>â
accelerate project delivery;\u003C/li>\u003Cli>â
reduce development costs;\u003C/li>\u003Cli>â
make production methods more reliable and standardized;\u003C/li>\u003Cli>â
limit technical debt;\u003C/li>\u003Cli>â
simplify the maintenance and evolution of data pipelines.\u003C/li>\u003C/ul>\u003Cp> \u003C/p>\n\u003Cp>In practice, our Data Engineers spend less time on plumbing and more on what matters: architecture, business understanding, solution design.\u003C/p>\u003Cp>\u003Cstrong>For our clients, this means pipelines delivered faster, at lower cost, with less debt to carry afterwards.\u003C/strong>\u003C/p>\u003Cp>This marks a new milestone for Moove-SI, one weâre excited to reach alongside PrettyWhale.ai ð\u003C/p>\u003Cp> \u003C/p>\n\u003Chr>\u003Ch2 id=\"moove-si\">\u003Cstrong>Moove-SI\u003C/strong>\u003C/h2>\u003Cp>\u003Ca href=\"https://moove-si.com/?ref=prettywhale.ai\" rel=\"noreferrer\">
1355Moove-SI\u003C/a> is an integrator specializing in Microsoft solutions. We support French and international companies in their digital transformation, the optimization of their business processes, and the adoption of new applications related to artificial intelligence.\u003C/p>\u003Cp>Thanks to our technical expertise and our understanding of business challenges, we design and deploy tailored solutions in the areas of sales, marketing, customer service, and business management.\u003C/p>\u003Cp>Our goal: to help our clients increase efficiency, improve performance, and take full advantage of the Microsoft ecosystem.\u003C/p>\u003Cp> \u003C/p>\n\u003Chr>\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-08-06T13:05:18.000+00:00","reading_time":2,"feature_image":"https://prettywhale.ai/content/images/2026/08/partnership-lockup-2.png","primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a6b435a393ec400015228a0","name":"Partners","slug":"partenariat","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#17285D","url":"https://prettywhale.ai/tag/partenariat/"}]},{"title":"Engineering AI, Copilots and Code Generators: A Taxonomy","slug":"engineering-ai-copilots-and-code-generators-a-taxonomy","excerpt":"\"AI writes code now\" covers four product families with four failure modes. The criterion that separates them, and four questions to pick the right one.","html":"\u003Cblockquote>\"AI writes code now\" describes four different products with four different failure modes.\u003C/blockquote>\u003Cp>A team that buys a copilot expecting a deliverable is disappointed. A team that evaluates a code generator the way it evaluates a copilot measures the wrong thing. And a team that assumes any of them removes the need to understand the code they ship learns otherwise, usually in production.\u003C/p>\u003Cp>This is an attempt at a taxonomy. Not a ranking, because the families answer different questions, and the honest answer to \"which one\" is usually \"which of these problems do you have\".\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"1-why-the-vocabulary-is-broken\">1. Why the vocabulary is broken\u003C/h2>\u003Cp>Four words do most of the damage:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>\"Assistant\"\u003C/strong> and \u003Cstrong>\"copilot\"\u003C/strong> are used both for tools that complete a line as you type and for tools that produce a whole file from a description. Those are different products.\u003C/li>\u003Cli>\u003Cstrong>\"Agent\"\u003C/strong> now means anything from a loop around a chat model to a system that opens a pull request. The word tells you nothing about what you receive.\u003C/li>\u003Cli>\u003Cstrong>\"Generation\"\u003C/strong> covers both a snippet and a runnable project with tests. The gap between those is most of the work.\u003C/li>\u003C/ul>\u003Cp>The result is that vendor comparisons happen at the level of model quality, which is the least discriminating variable available, because everyone has access to similar models.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"2-the-criterion-that-actually-separates-the-families\">2. The criterion that actually separates the families\u003C/h2>\u003Cp>The useful question is not how the tool works. It is what it hands you, and who owns what happens next.\u003C/p>\u003Cp>Two dimensions:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Unit of output.\u003C/strong> A completion, a file, a project, or a running service.\u003C/li>\u003Cli>\u003Cstrong>Who carries the integration burden.\u003C/strong> You, or the tool.\u003C/li>\u003C/ul>\u003Cp>That second dimension is the one that gets ignored, and it is where the cost lives. A tool that produces good code you then have to wire in, test, document and make deployable has handed you a starting point. Whether that is a good deal depends entirely on how much of the remaining work you were going to do anyway.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"3-the-four-families\">3. The four families\u003C/h2>\n\u003C!--kg-card-begin: html-->\n\u003Ctable style=\"width:100%;border-collapse:collapse;\">\n \u003Cthead>\n \u003Ctr>\n \u003Cth style=\"border:1px solid #94a3b8;padding:10px 12px;text-align:left;\">
1355Family\u003C/th>\n \u003Cth style=\"border:1px solid #94a3b8;padding:10px 12px;text-align:left;\">Unit of output\u003C/th>\n \u003Cth style=\"border:1px solid #94a3b8;padding:10px 12px;text-align:left;\">Integration burden\u003C/th>\n \u003Cth style=\"border:1px solid #94a3b8;padding:10px 12px;text-align:left;\">Typical examples\u003C/th>\n \u003C/tr>\n \u003C/thead>\n \u003Ctbody>\n \u003Ctr>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">\u003Cstrong>In-editor completion\u003C/strong>\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">A line, a block, a function\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Entirely on you\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">GitHub Copilot, Cursor tab completion, JetBrains AI\u003C/td>\n \u003C/tr>\n \u003Ctr>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">\u003Cstrong>Conversational generation - Copilots\u003C/strong>\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">A file, a snippet, an explanation\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Entirely on you\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Claude Code, Antigravity 2.0, Cline, Cursor\u003C/td>\n \u003C/tr>\n \u003Ctr>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">\u003Cstrong>Specialized generation - Engineering AI\u003C/strong>\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">A complete project or component, with its supporting artefacts\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Mostly on the tool\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Domain-specific generators, PrettyWhale.AI in the ingestion domain\u003C/td>\n \u003C/tr>\n \u003Ctr>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">\u003Cstrong>Managed platform\u003C/strong>\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">A running service, no code exposed\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">On the vendor, and so is the lock-in\u003C/td>\n \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Fivetran, Airbyte Cloud, hyperscaler managed services\u003C/td>\n \u003C/tr>\n \u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>The two middle families get conflated most often, and they differ on the dimension that matters. A conversational model produces plausible code fast, and leaves you to determine whether it handles the cases you did not mention. A specialized generator produces a narrower range of things, and is accountable for the artefacts around the code.\u003C/p>\u003Cp>The fourth family is included deliberately, because for a large share of data ingestion work it is the correct answer.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"4-what-each-family-is-genuinely-good-at\">4. What each family is genuinely good at\u003C/h2>\u003Cp>\u003Cstrong>In-editor completion.\u003C/strong> Best-in-class at reducing keystrokes on code you already know how to write. The value is real and it compounds across a working day. What it is not good at is the code you do not know how to write, because you cannot evaluate a suggestion in a domain you are unfamiliar with. That is the failure mode: a plausible completion in unfamiliar territory is more dangerous than no completion.\u003C/p>\u003Cp>\u003Cstrong>Conversational generation.\u003C/strong> Best-in-class at breadth. Any language, any framework, any problem, immediately, plus an explanation. Unbeatable for exploration, for unfamiliar syntax, for a first pass at something you will then rewrite. The failure mode is the one everyone has met: code that runs on the example and quietly omits the handling of everything the prompt did not mention. It optimizes for a working demonstration, because that is what it was rewarded for.\u003C/p>\u003Cp>\u003Cstrong>Specialized generation.\u003C/strong> Best at producing the full artefact set for a bounded problem, because the boundary is what makes the completeness possible. If a system only ever produces one kind of thing, it can be accountable for the tests, the schema and the documentation of that thing. The trade is scope: outside its domain it does nothing. And the honest risk is that a complete-looking deliverable invites less scrutiny than a snippet.\u003C/p>\u003Cp>\u003Cstrong>Managed platform.\u003C/strong> Best when the source is in the catalogue and you would rather not own the code. That combination covers a lot of real situations, and treating it as a compromise is a mistake. The failure modes are known and they are commercial rather than technical: cost at volume, and the sources the catalogue does not cover.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"5-each-family-fails-a-different-evaluation\">5. Each family fails a different evaluation\u003C/h2>\u003Cp>The most common tooling mistake is not choosing the wrong family. It is evaluating one family with the criteria of another, which produces a confident decision based on a measurement that did not apply.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Family\u003C/th>\n\u003Cth>
1355The wrong test\u003C/th>\n\u003Cth>The test that tells you something\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>In-editor completion\u003C/strong>\u003C/td>\n\u003Ctd>Asking it to produce a whole feature\u003C/td>\n\u003Ctd>Have two engineers work a normal day with and without it, in code they know\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Conversational generation - Copilots\u003C/strong>\u003C/td>\n\u003Ctd>Judging the first answer to a well-written prompt\u003C/td>\n\u003Ctd>Take the output and count what you had to add before it could ship\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Specialized generation - Engineering AI\u003C/strong>\u003C/td>\n\u003Ctd>Comparing its snippet quality against a general model\u003C/td>\n\u003Ctd>Run it on a source you have already delivered by hand, and compare the full artefact set against what you shipped\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Managed platform\u003C/strong>\u003C/td>\n\u003Ctd>Testing it on a source in the catalogue\u003C/td>\n\u003Ctd>Testing it on your three most awkward sources, and pricing it at next year's volume\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp> \u003C/p>\n\u003Ch2 id=\"6-choosing-four-questions\">6. Choosing: four questions\u003C/h2>\u003Cp>Not \"which tool is best\", but \"which of these is true for you\".\u003C/p>\u003Cp>\u003Cstrong>Is the code you need in a domain your team knows well?\u003C/strong> If yes, completion tools give you the most value per euro, and generation adds process overhead for little gain.\u003C/p>\u003Cp>\u003Cstrong>Are your sources in a managed platform's catalogue?\u003C/strong> If yes, and you have no strong portability requirement, buy the platform. Building what you can buy is a common and expensive error.\u003C/p>\u003Cp>\u003Cstrong>Do you need to own and deploy the code?\u003C/strong> Compliance, air-gapped environments, exit-risk policies, or wanting to modify behaviour without waiting for a vendor. If yes, the managed family is out regardless of its other merits.\u003C/p>\u003Cp>\u003Cstrong>Is the work repetitive across many similar instances?\u003C/strong> Fifty sources of the same shape is where specialized generation earns its keep. Three sources is probably not worth changing your process for.\u003C/p>\u003Cblockquote>\u003Cstrong>A note on how each family is priced\u003C/strong>\u003Cbr>Pricing model shapes the decision more than most evaluations account for, because it determines who absorbs the cost of growth. Completion tools are priced per seat, which makes the cost predictable and independent of how much you use them. \u003Cbr>Conversational tools are priced per seat or per token, and the token variant means your bill moves with your usage in ways that are hard to forecast. Specialized generators tend to be priced per unit of output or per seat, which ties cost to work delivered. Some managed platforms are priced on data volume, the model that surprises teams most often, because volume grows on its own and the bill grows with it.\u003Cbr>The practical consequence: a comparison run at today's volume can invert entirely at next year's. Volume pricing is defensible for a managed service, but it's a reason to run the arithmetic at the scale you expect rather than the scale you have.\u003C/blockquote>\u003Cp> \u003C/p>\n\u003Ch2 id=\"7-where-the-categories-are-converging\">7. Where the categories are converging\u003C/h2>\u003Cp>Two movements worth watching, because they will make this taxonomy less useful over time.\u003C/p>\u003Cp>\u003Cstrong>Completion tools are moving up.\u003C/strong> Editor-integrated agents now open branches and produce multi-file changes. The unit of output is growing, which pushes them toward the generation families and raises the same question about accountability for tests and documentation.\u003C/p>\u003Cp>\u003Cstrong>Managed platforms are moving toward code.\u003C/strong> Several now offer connector development frameworks, which is an admission that the catalogue will never be complete. That narrows the gap from the other direction.\u003C/p>\u003Cp>What will not converge is the criterion in section 2. Whatever the tools are called in two years, the question of what you receive and who owns the remaining work will still separate them, and it will still be the question vendor comparisons avoid.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"what-this-taxonomy-leaves-out\">What this taxonomy leaves out\u003C/h2>\u003Cp>Two things, named because a classification that pretends to be complete is less useful than one that states its edges.\u003C/p>\u003Cp>\u003Cstrong>It says nothing about model quality.\u003C/strong> Deliberately. Model capability moves every few months and it is broadly shared across vendors, so a taxonomy built on it would be obsolete before it was published. What persists is the shape of what you receive.\u003C/p>\u003Cp>\u003Cstrong>It does not cover tools that generate things other than code.\u003C/strong> Test generation, documentation generation, schema inference, and query assistants all exist and none of them fit cleanly here. A team assembling a toolchain will end up combining families and categories, which is normal and not a flaw in the choice.\u003C/p>\u003Cp> \u003C/p>\n\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Four families, separated by unit of output and by who carries the integration burden. Completion for code you already know. Conversation for breadth and exploration. Specialized generation for repetitive, bounded, artefact-heavy work. Managed platforms when the catalogue covers you and ownership does not matter.\u003C/p>\u003Cp>Most teams need more than one. The mistake is not picking wrong, it is picking a family whose failure mode you have not thought about.\u003C/p>\u003Cp>And the question that outranks all of it: whoever reviews the output, do they know what a production-grade version of this looks like. No family removes that requirement, and the ones that produce the most complete-looking output are the ones where forgetting it costs the most.\u003C/p>\u003Cp> \u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai sits in the third family, for one domain: ingestion and integration code. Given a source sample it produces the code, unit tests, data quality checks, schemas, configuration, orchestration and documentation, as portable code you own.\u003C/em>\u003C/p>\u003Cp> \u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any question on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-08-05T14:20:22.000+00:00","reading_time":6,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"The End of the Daily-Rate Model for IT Services","slug":"the-end-of-the-it-services-company-model-based-solely-on-daily-rates","excerpt":"For thirty years IT services firms sold human time by the day. Clients now ask what you save them, not how many consultants you can put on the project.","html":"\u003Cp>For more than thirty years, the economic model of IT services companies (ESNs in France) has relied on a simple equation: selling human time. The arrival of AI profoundly changes this equation. When an engineer can produce in a few hours what previously required several days, a question becomes inevitable: Should the client continue to buy time or start buying results?\u003C/p>\u003Cp>The paradox is striking: The more productive an IT services company becomes thanks to AI, the fewer days it theoretically needs to sell. For the first time in the history of digital services, the production of part of the technical work is no longer exclusively linked to the time spent by an engineer.\u003C/p>\u003Cp>AI is capable of producing code, generating tests, writing documentation, creating configurations, performing data analysis, or even automating tasks previously performed manually. In this new context, the end client is no longer interested in the number of days consumed. They are interested in the result obtained.\u003C/p>\u003Cp>The question is no longer:\u003C/p>\u003Cp>â¡ï¸ \"How many consultants are you going to mobilize?\"\u003C/p>\u003Cp>but:\u003C/p>\u003Cp>â¡ï¸ \"How much time are you going to save me?\"\u003C/p>\u003Cp>and above all:\u003C/p>\u003Cp>â¡ï¸ \"How much savings are you going to allow me to achieve?\"\u003C/p>\u003Cp>This evolution directly challenges the historic Daily Rate (TJM) model. Indeed, when a task previously performed in twenty days can be executed in two days thanks to AI, the client no longer understands why they should continue to pay for twenty days of service. The billing logic based exclusively on time then progressively becomes incompatible with the productivity gains brought by artificial intelligence.\u003C/p>\u003Cp>IT services companies that continue to sell only time risk finding themselves in a paradoxical situation: the more productive their teams become thanks to AI, the more they mechanically reduce their volume of billable days. Conversely, companies that know how to sell value rather than time will have a considerable advantage.\u003C/p>\u003Cp>Tomorrow, the most successful economic models will probably be hybrid, with a portion linked to:\u003C/p>\u003Cul>\u003Cli> Human expertise;\u003C/li>\u003Cli>The use of AI platforms;\u003C/li>\u003Cli> The results obtained;\u003C/li>\u003Cli> The actual uses of the technology.\u003C/li>\u003C/ul>\u003Cp>The IT services company will no longer be just a supplier of consultants. It will become a productivity operator. Its mission will no longer consist of selling man-days, but of delivering a business result faster, at a lower cost, and with a higher level of quality.\u003C/p>\u003Cp> \u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-06-17T11:05:14.000+00:00","reading_time":2,"feature_image":"https://prettywhale.ai/content/images/2026/06/image--3--1.png","primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Ingestion vs Integration: A Confusion That Costs Millions","slug":"ingestion-vs-integration-code","excerpt":"Ingestion and integration code get used as synonyms. They solve different problems, and the teams that optimise the wrong one pay for it in production.","html":"\u003Cp>In many Data projects, the terms ingestion and integration are used as synonyms. \u003C/p>\u003Cp>Yet, they designate two radically different realities. \u003C/p>\u003Cp>And this confusion is at the root of a large part of the cost overruns, delays, and quality problems that companies face today.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"ingestion-code-the-foundation\">Ingestion code: the foundation\u003C/h2>\u003Cp>The ingestion code is responsible for collecting, controlling, standardizing, and preparing data as soon as it enters the information system. It is what:\u003C/p>\u003Cul>\u003Cli>reads files, APIs, databases, or external streams;\u003C/li>\u003Cli>detects anomalies;\u003C/li>\u003Cli>applies quality rules;\u003C/li>\u003Cli>converts formats;\u003C/li>\u003Cli>manages errors;\u003C/li>\u003Cli>guarantees that the data entering is consistent and usable.\u003C/li>\u003C/ul>\u003Cp>In other words, ingestion determines the quality of the raw material.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"integration-code-the-assembly\">Integration code: the assembly\u003C/h2>\u003Cp>The integration code comes into play next. Its role is to combine, enrich, reconcile, and distribute data from different sources. For example, it allows to:\u003C/p>\u003Cul>\u003Cli>cross-reference CRM and ERP data;\u003C/li>\u003Cli>feed a Data Warehouse;\u003C/li>\u003Cli>produce business views;\u003C/li>\u003Cli>feed applications or AI models.\u003C/li>\u003C/ul>\u003Cp>Integration does not create quality. It simply leverages what it is provided with.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"the-problem-many-companies-optimize-the-wrong-subject\">The problem: many companies optimize the wrong subject\u003C/h2>\u003Cp>When erroneous, incomplete, or inconsistent data enters the system, no integration can magically transform it into reliable data. Poor ingestion produces:\u003C/p>\u003Cul>\u003Cli>inconsistent repositories;\u003C/li>\u003Cli>incorrect calculations;\u003C/li>\u003Cli>misleading dashboards;\u003C/li>\u003Cli>biased AI models;\u003C/li>\u003Cli>skyrocketing maintenance costs.\u003C/li>\u003C/ul>\u003Cp>Integration then only accelerates the propagation of errors.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"garbage-in-garbage-out\">\"Garbage In, Garbage Out\"\u003C/h2>\u003Cp>This decades-old computer science rule still holds true. If the ingestion is bad:\u003C/p>\u003Cul>\u003Cli>the Data Warehouse will be bad;\u003C/li>\u003Cli>the indicators will be bad;\u003C/li>\u003Cli>the analyses will be bad;\u003C/li>\u003Cli>the AI will be bad.\u003C/li>\u003C/ul>\u003Cp>Regardless of the sophistication of the tools used afterward. No technological layer can sustainably compensate for data that is degraded upon arrival.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"why-ingestion-is-becoming-a-strategic-stake\">Why ingestion is becoming a strategic stake\u003C/h2>\u003Cp>For years, the Data industry has invested heavily in:\u003C/p>\u003Cul>\u003Cli>orchestration;\u003C/li>\u003Cli>governance;\u003C/li>\u003Cli>cataloging;\u003C/li>\u003Cli>observability;\u003C/li>\u003Cli>artificial intelligence.\u003C/li>\u003C/ul>\u003Cp>But the real starting point remains the same: the quality of the ingestion code. Because it is what determines whether the data entering the company is reliable or not. An organization that masters its ingestion reduces its costs, accelerates its Data projects, and mechanically increases trust in its analyses and AI systems.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"conclusion\">Conclusion\u003C/h2>\u003Cp>We can build the best integrations in the world. We can deploy the most modern platforms. We can connect hun
1355dreds of systems. But one truth remains: an excellent integration applied to a poor ingestion only produces bad data faster.\u003C/p>\u003Cp>The quality of a Data system does not begin with integration. It begins with ingestion. Integration transports data. Ingestion creates its quality. If the second fails, the first only accelerates the disaster.\u003C/p>\u003Cp> \u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive kg-cta-centered\" data-layout=\"immersive\">\n \n \u003Cdiv class=\"kg-cta-content\">\n \n \n \u003Cdiv class=\"kg-cta-content-inner\">\n \n \u003Cdiv class=\"kg-cta-text\">\n \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n \u003C/div>\n \n \n \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n Contact us\n \u003C/a>\n \n \u003C/div>\n \n \u003C/div>\n \u003C/div>","published_at":"2026-06-12T10:00:00.000+00:00","reading_time":2,"feature_image":"https://prettywhale.ai/content/images/2026/06/Capture-d-----cran-2026-06-16-175101.png","primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]}],"headers":{"headers":{},"normalizedNames":{},"lazyUpdate":null},"status":200,"statusText":"OK","url":"/api/posts"},"__nghData__":[{"t":{"21":"t0"},"c":{"21":[{"i":"t0","r":1,"t":{"1":"t1","2":"t2"},"c":{"1":[],"2":[{"i":"t2","r":1}]},"x":4},{"i":"t0","r":1,"t":{"1":"t1","2":"t2"},"c":{"1":[{"i":"t1","r":1}],"2":[]}}]}},{},{"t":{"16":"t3"},"c":{"16":[{"i":"t3","r":1,"x":5}]}},{"t":{"7":"t4"},"c":{"7":[{"i":"t4","r":1,"x":4}]}},{"t":{"2":"t5","3":"t8"},"c":{"2":[{"i":"t5","r":3,"t":{"0":"t6","1":"t7"},"c":{"0":[],"1":[{"i":"t7","r":1}]},"x":5}],"3":[]}},{"t":{"1":"t10","5":"t11","8":"t13"},"c":{"1":[{"i":"t10","r":1}],"5":[{"i":"t11","r":1,"t":{"1":"t12"},"c":{"1":[{"i":"t12","r":2}]}}],"8":[]}},{"t":{"10":"t9"},"c":{"10":[{"i":"t9","r":1,"x":12}]}},{"t":{"14":"t14"},"c":{"14":[{"i":"t14","r":3,"t":{"0":"t15","1":"t16"},"c":{"0":[],"1":[{"i":"t16","r":1}]},"x":4},{"i":"t14","r":3,"t":{"0":"t15","1":"t16"},"c":{"0":[{"i":"t15","r":1}],"1":[]}}]}},{"c":{"0":[{"i":"c411397059","r":1}]}}]}</script>
1355</body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.