Why does code written for one chip break on another of the same type?
Because the tag type fixes the commands, not the memory map. Two Type 2 tags answer the same READ and put their configuration in different pages, and a hardcoded address that worked on one lands in user memory on the other. Read what the tag reports instead.
What the type fixes
An NFC Forum tag type is a contract about commands: which ones a reader may send, how memory is addressed, how an NDEF message is found. The Type 2 page and the Type 4 page set out those two contracts.
What it does not fix is everything a chip adds: how many pages exist, where the configuration lives, whether there is a counter, a mirror or a password, and which extra commands answer. Those belong to the chip, and this site leaves them to the chip fiches, as the configuration page does for the pages after the data area.
What the tag reports
Most of what code needs is readable rather than assumable. On Android, Ndef answers three questions without touching the radio: getType() returns the NFC Forum type, getMaxSize() the largest message the tag accepts, and isWritable() whether it still takes a write. MifareUltralight.getType() distinguishes an Ultralight from an Ultralight C, and returns unknown when it cannot tell.
Below that, the tag itself answers. The capability container of a Type 2 tag gives the size of the data area and its access condition, and Get System Information on an ISO/IEC 15693 tag gives the block size and the block count, which the 15693 page reads before anything else. A program that asks these three sources has very little left to assume.
The same family, two maps
The clearest case is one family. An NTAG213 has 45 pages, its dynamic lock bytes in page 40 and its configuration in pages 41 to 44; an NTAG215 has 135 pages and keeps the same settings in pages 130 to 134, as their fiches on wakdev.com describe. Page 41 is configuration on one and ordinary user memory on the other.
Code that writes a configuration by page number therefore corrupts content on the wrong chip, silently, and the tag keeps reading as if nothing happened. Which is why the configuration page asks for the chip to be identified before the sequence runs.
Read first, write second
Detect features rather than models. Ask the tag what it is and what it offers, adjust, and refuse to continue when the answer is not one your code knows: an explicit refusal is a bug report, a silent write is a corrupted batch.
And keep a tag of each chip in the test set, which the test set page asks for anyway. One tag of a second size in the same family finds this class of bug in a minute, and nothing else finds it before production does.
Frequently asked questions
Is the tag type not enough to write portable code?
For NDEF, yes. For anything below it, no: pages, sizes and configuration addresses belong to the chip rather than to the type.
What can I read instead of assuming?
The type, the maximum size and the writability from the platform, the capability container on a Type 2 tag, and the system information on an ISO/IEC 15693 tag.
Why did my configuration write corrupt the content?
The page number belonged to another chip of the same family. On a larger chip that address is user memory.
How do I catch this before production?
Keep two chips of the same type and different sizes in the test set, and run the sequence on both.